diff --git a/app/src/components/layout/3D-cards/cards/ProductionCapacity.tsx b/app/src/components/layout/3D-cards/cards/ProductionCapacity.tsx index a719023..cc398e2 100644 --- a/app/src/components/layout/3D-cards/cards/ProductionCapacity.tsx +++ b/app/src/components/layout/3D-cards/cards/ProductionCapacity.tsx @@ -35,19 +35,32 @@ interface ProductionCapacityProps { // onPointerDown:any } -const ProductionCapacity: React.FC = ({ id, type, position, rotation, onContextMenu }) => { +const ProductionCapacity: React.FC = ({ + id, + type, + position, + rotation, + onContextMenu, +}) => { const { selectedChartId, setSelectedChartId } = useWidgetStore(); - const { measurements: chartMeasurements, duration: chartDuration, name: widgetName } = useChartStore(); + const { + measurements: chartMeasurements, + duration: chartDuration, + name: widgetName, + } = useChartStore(); const [measurements, setmeasurements] = useState({}); - const [duration, setDuration] = useState("1h") - const [name, setName] = useState("Widget") - const [chartData, setChartData] = useState<{ labels: string[]; datasets: any[] }>({ + const [duration, setDuration] = useState("1h"); + const [name, setName] = useState("Widget"); + const [chartData, setChartData] = useState<{ + labels: string[]; + datasets: any[]; + }>({ labels: [], datasets: [], }); const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL; const email = localStorage.getItem("email") || ""; - const organization = email?.split("@")[1]?.split(".")[0] + const organization = email?.split("@")[1]?.split(".")[0]; // Chart data for a week const defaultChartData = { labels: ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"], // Days of the week @@ -101,7 +114,8 @@ const ProductionCapacity: React.FC = ({ id, type, posit }; useEffect(() => { - if (!iotApiUrl || !measurements || Object.keys(measurements).length === 0) return; + if (!iotApiUrl || !measurements || Object.keys(measurements).length === 0) + return; const socket = io(`http://${iotApiUrl}`); @@ -111,7 +125,6 @@ const ProductionCapacity: React.FC = ({ id, type, posit interval: 1000, }; - const startStream = () => { socket.emit("lineInput", inputData); }; @@ -148,22 +161,20 @@ const ProductionCapacity: React.FC = ({ id, type, posit }, [measurements, duration, iotApiUrl]); const fetchSavedInputes = async () => { - if (id !== "") { try { - const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/widget3D/${id}/${organization}`); + const response = await axios.get( + `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/widget3D/${id}/${organization}` + ); if (response.status === 200) { - setmeasurements(response.data.Data.measurements) - setDuration(response.data.Data.duration) - setName(response.data.widgetName) + setmeasurements(response.data.Data.measurements); + setDuration(response.data.Data.duration); + setName(response.data.widgetName); } else { - } - } catch (error) { - - } + } catch (error) { } } - } + }; useEffect(() => { fetchSavedInputes(); @@ -173,13 +184,9 @@ const ProductionCapacity: React.FC = ({ id, type, posit if (selectedChartId?.id === id) { fetchSavedInputes(); } - } - , [chartMeasurements, chartDuration, widgetName]) + }, [chartMeasurements, chartDuration, widgetName]); - useEffect(() => { - - - }, [rotation]) + useEffect(() => { }, [rotation]); const rotationDegrees = { x: (rotation[0] * 180) / Math.PI, y: (rotation[1] * 180) / Math.PI, @@ -187,30 +194,44 @@ const ProductionCapacity: React.FC = ({ id, type, posit }; const transformStyle = { - transform: `rotateX(${rotationDegrees.x}deg) rotateY(${rotationDegrees.y}deg) rotateZ(${rotationDegrees.z}deg)`, + transform: `rotateX(${rotationDegrees.x}deg) rotateY(${rotationDegrees.y}deg) rotateZ(${rotationDegrees.z}deg) translate(-50%, -50%)`, }; return ( - -
{ + e.preventDefault(); + e.stopPropagation(); + }} + onDrop={(e) => { + e.preventDefault(); + // e.stopPropagation(); + }} + wrapperClass="pointer-none" + className={`${selectedChartId?.id === id ? "activeChart" : ""}`} + > +
setSelectedChartId({ id: id, type: type })} onContextMenu={onContextMenu} + style={{ - width: '300px', // Original width - height: '300px', // Original height + width: "300px", // Original width + height: "300px", // Original height transform: transformStyle.transform, - transformStyle: 'preserve-3d' + transformStyle: "preserve-3d", + position: "absolute", }} >
@@ -233,10 +254,18 @@ const ProductionCapacity: React.FC = ({ id, type, posit
{" "}
{/* Bar Chart */} - 0 ? chartData : defaultChartData} options={chartOptions} /> + 0 + ? chartData + : defaultChartData + } + options={chartOptions} + />
+ ); }; diff --git a/app/src/components/layout/3D-cards/cards/ReturnOfInvestment.tsx b/app/src/components/layout/3D-cards/cards/ReturnOfInvestment.tsx index 8e6c707..9019aaf 100644 --- a/app/src/components/layout/3D-cards/cards/ReturnOfInvestment.tsx +++ b/app/src/components/layout/3D-cards/cards/ReturnOfInvestment.tsx @@ -226,6 +226,7 @@ const ReturnOfInvestment: React.FC = ({ id, type, posit transition: 'transform 0.1s ease-out' }} + className={`${selectedChartId?.id === id ? "activeChart" : ""}`} >
setSelectedChartId({ id: id, type: type })} diff --git a/app/src/components/layout/3D-cards/cards/StateWorking.tsx b/app/src/components/layout/3D-cards/cards/StateWorking.tsx index 9adf77f..93b86a2 100644 --- a/app/src/components/layout/3D-cards/cards/StateWorking.tsx +++ b/app/src/components/layout/3D-cards/cards/StateWorking.tsx @@ -111,6 +111,7 @@ const StateWorking: React.FC = ({ id, type, position, rotatio transition: 'transform 0.1s ease-out' }} + className={`${selectedChartId?.id === id ? "activeChart" : ""}`} >
setSelectedChartId({ id: id, type: type })} diff --git a/app/src/components/layout/3D-cards/cards/Throughput.tsx b/app/src/components/layout/3D-cards/cards/Throughput.tsx index de3109b..f51b052 100644 --- a/app/src/components/layout/3D-cards/cards/Throughput.tsx +++ b/app/src/components/layout/3D-cards/cards/Throughput.tsx @@ -206,6 +206,7 @@ const Throughput: React.FC = ({ id, type, position, rotation, o transition: 'transform 0.1s ease-out' }} + className={`${selectedChartId?.id === id ? "activeChart" : ""}`} >
setSelectedChartId({ id: id, type: type })} diff --git a/app/src/components/layout/sidebarLeft/visualization/Templates.tsx b/app/src/components/layout/sidebarLeft/visualization/Templates.tsx index b99b297..5eb8d4e 100644 --- a/app/src/components/layout/sidebarLeft/visualization/Templates.tsx +++ b/app/src/components/layout/sidebarLeft/visualization/Templates.tsx @@ -3,13 +3,10 @@ import { useDroppedObjectsStore } from "../../../../store/useDroppedObjectsStore import useTemplateStore from "../../../../store/useTemplateStore"; import { useSelectedZoneStore } from "../../../../store/useZoneStore"; import { getTemplateData } from "../../../../services/realTimeVisulization/zoneData/getTemplate"; -import { deleteTemplateApi } from "../../../../services/realTimeVisulization/zoneData/deleteTemplate"; -import { loadTempleteApi } from "../../../../services/realTimeVisulization/zoneData/loadTemplate"; import { useSocketStore } from "../../../../store/store"; const Templates = () => { - const { templates, removeTemplate } = useTemplateStore(); - const { setTemplates } = useTemplateStore(); + const { templates, removeTemplate, setTemplates } = useTemplateStore(); const { setSelectedZone, selectedZone } = useSelectedZoneStore(); const { visualizationSocket } = useSocketStore(); @@ -35,15 +32,11 @@ const Templates = () => { let deleteTemplate = { organization: organization, templateID: id, - } + }; if (visualizationSocket) { - visualizationSocket.emit("v2:viz-template:deleteTemplate", deleteTemplate) + visualizationSocket.emit("v2:viz-template:deleteTemplate", deleteTemplate); } removeTemplate(id); - // let response = await deleteTemplateApi(id, organization); - - // if (response.message === "Template deleted successfully") { - // } } catch (error) { console.error("Error deleting template:", error); } @@ -60,114 +53,54 @@ const Templates = () => { organization: organization, zoneId: selectedZone.zoneId, templateID: template.id, - } - console.log('template: ', template); - console.log('loadingTemplate: ', loadingTemplate); + }; if (visualizationSocket) { - visualizationSocket.emit("v2:viz-template:addToZone", loadingTemplate) + visualizationSocket.emit("v2:viz-template:addToZone", loadingTemplate); } - // let response = await loadTempleteApi(template.id, selectedZone.zoneId, organization); - // if (response.message === "Template placed in Zone") { - setSelectedZone({ - panelOrder: template.panelOrder, - activeSides: Array.from(new Set(template.panelOrder)), // No merging with previous `activeSides` - widgets: template.widgets, + setSelectedZone({ + panelOrder: template.panelOrder, + activeSides: Array.from(new Set(template.panelOrder)), // No merging with previous `activeSides` + widgets: template.widgets, + }); + + useDroppedObjectsStore.getState().setZone(selectedZone.zoneName, selectedZone.zoneId); + + if (Array.isArray(template.floatingWidget)) { + template.floatingWidget.forEach((val: any) => { + useDroppedObjectsStore.getState().addObject(selectedZone.zoneName, val); }); - - useDroppedObjectsStore.getState().setZone(selectedZone.zoneName, selectedZone.zoneId); - - if (Array.isArray(template.floatingWidget)) { - template.floatingWidget.forEach((val: any) => { - useDroppedObjectsStore.getState().addObject(selectedZone.zoneName, val); - }); - } - // } + } } catch (error) { console.error("Error loading template:", error); } }; - return ( -
+
{templates.map((template) => ( -
+
{template?.snapshot && ( -
- {" "} - {/* 16:9 aspect ratio */} +
{`${template.name} handleLoadTemplate(template)} />
)} -
+
handleLoadTemplate(template)} - style={{ - cursor: "pointer", - fontWeight: "500", - // ':hover': { - // textDecoration: 'underline' - // } - }} + className="template-name" > {template.name}
))} {templates.length === 0 && ( -
+
No saved templates yet. Create one in the visualization view!
)} @@ -192,4 +118,3 @@ const Templates = () => { }; export default Templates; - diff --git a/app/src/components/layout/sidebarLeft/visualization/widgets/Widgets3D.tsx b/app/src/components/layout/sidebarLeft/visualization/widgets/Widgets3D.tsx index 6872497..c54f75c 100644 --- a/app/src/components/layout/sidebarLeft/visualization/widgets/Widgets3D.tsx +++ b/app/src/components/layout/sidebarLeft/visualization/widgets/Widgets3D.tsx @@ -21,11 +21,13 @@ const Widgets3D = () => { className="widget-item" draggable onDragStart={(e) => { + let name = widget.name let crt = e.target if (crt instanceof HTMLElement) { const widget = crt.cloneNode(true) as HTMLElement; e.dataTransfer.setDragImage(widget, 0, 0) e.dataTransfer.effectAllowed = "move" + e.dataTransfer.setData("text/plain", "ui-" + name) } }} onPointerDown={() => { @@ -40,7 +42,7 @@ const Widgets3D = () => { className="widget-image" src={widget.img} alt={widget.name} - // draggable={false} + draggable={false} />
))} diff --git a/app/src/components/layout/sidebarLeft/visualization/widgets/WidgetsFloating.tsx b/app/src/components/layout/sidebarLeft/visualization/widgets/WidgetsFloating.tsx index 8c3df32..09c481a 100644 --- a/app/src/components/layout/sidebarLeft/visualization/widgets/WidgetsFloating.tsx +++ b/app/src/components/layout/sidebarLeft/visualization/widgets/WidgetsFloating.tsx @@ -46,7 +46,7 @@ const WidgetsFloating = () => { ))} */} {/* Floating 1 */} { const { toggleUI } = useToggleStore(); const { selectedActionSphere } = useSelectedActionSphere(); const { subModule, setSubModule } = useSubModuleStore(); - const { selectedFloorItem } = useselectedFloorItem(); + const { selectedFloorItem } = useSelectedFloorItem(); // Reset activeList whenever activeModule changes useEffect(() => { if (activeModule !== "simulation") setSubModule("properties"); @@ -101,7 +101,7 @@ const SideBarRight: React.FC = () => { )} {toggleUI && subModule === "zoneProperties" && - activeModule === "builder" && ( + (activeModule === "builder" || activeModule === "simulation") && (
@@ -135,7 +135,7 @@ const SideBarRight: React.FC = () => { selectedActionSphere.path.type === "StaticMachine" && (
- +
)} @@ -144,7 +144,7 @@ const SideBarRight: React.FC = () => { selectedActionSphere.path.type === "ArmBot" && (
- +
)} diff --git a/app/src/components/layout/sidebarRight/mechanics/ArmBotMechanics.tsx b/app/src/components/layout/sidebarRight/mechanics/ArmBotMechanics.tsx index 744d555..4dffdb2 100644 --- a/app/src/components/layout/sidebarRight/mechanics/ArmBotMechanics.tsx +++ b/app/src/components/layout/sidebarRight/mechanics/ArmBotMechanics.tsx @@ -1,65 +1,121 @@ -import React, { useRef, useMemo } from "react"; +import React, { useRef, useMemo, useCallback, useState } from "react"; import { InfoIcon } from "../../../icons/ExportCommonIcons"; import InputWithDropDown from "../../../ui/inputs/InputWithDropDown"; -import { useEditingPoint, useEyeDropMode, usePreviewPosition, useSelectedActionSphere, useSimulationStates, useSocketStore } from "../../../../store/store"; +import { + useSelectedActionSphere, + useSimulationStates, + useSocketStore +} from "../../../../store/store"; import * as Types from '../../../../types/world/worldTypes'; -import PositionInput from "../customInput/PositionInputs"; -import { setEventApi } from "../../../../services/factoryBuilder/assest/floorAsset/setEventsApt"; +import LabeledButton from "../../../ui/inputs/LabledButton"; +import LabledDropdown from "../../../ui/inputs/LabledDropdown"; const ArmBotMechanics: React.FC = () => { const { selectedActionSphere } = useSelectedActionSphere(); const { simulationStates, setSimulationStates } = useSimulationStates(); const { socket } = useSocketStore(); + const [selectedTrigger, setSelectedTrigger] = useState(null); const propertiesContainerRef = useRef(null); - const { selectedPoint, connectedPointUuids } = useMemo(() => { - if (!selectedActionSphere?.points?.uuid) return { selectedPoint: null, connectedPointUuids: [] }; + // Get connected models for dropdowns + const connectedModels = useMemo(() => { + if (!selectedActionSphere?.points?.uuid) return []; - const vehiclePaths = simulationStates.filter( + }, [selectedActionSphere, simulationStates]); + + // Get triggers only from connected models + const connectedTriggers = useMemo(() => { + }, [connectedModels, simulationStates]); + + const { selectedPoint } = useMemo(() => { + if (!selectedActionSphere?.points?.uuid) return { selectedPoint: null }; + + const armBotPaths = simulationStates.filter( (path): path is Types.ArmBotEventsSchema => path.type === "ArmBot" ); - const points = vehiclePaths.find( + const points = armBotPaths.find( (path) => path.points.uuid === selectedActionSphere.points.uuid )?.points; - if (!points) return { selectedPoint: null, connectedPointUuids: [] }; - - const connectedUuids: string[] = []; - if (points.connections?.targets) { - points.connections.targets.forEach(target => { - connectedUuids.push(target.pointUUID); - }); - } - return { - selectedPoint: points, - connectedPointUuids: connectedUuids + selectedPoint: points || null }; }, [selectedActionSphere, simulationStates]); const updateBackend = async (updatedPath: Types.ArmBotEventsSchema | undefined) => { - if (!updatedPath) return; - const email = localStorage.getItem("email"); - const organization = email ? email.split("@")[1].split(".")[0] : ""; + // if (!updatedPath) return; + // const email = localStorage.getItem("email"); + // const organization = email ? email.split("@")[1].split(".")[0] : ""; - // await setEventApi( - // organization, - // updatedPath.modeluuid, - // { type: "ArmBot", points: updatedPath.points } - // ); - - const data = { - organization: organization, - modeluuid: updatedPath.modeluuid, - eventData: { type: "ArmBot", points: updatedPath.points } - } - - socket.emit('v2:model-asset:updateEventData', data); + // const data = { + // organization: organization, + // modeluuid: updatedPath.modeluuid, + // eventData: { type: "ArmBot", points: updatedPath.points } + // } + // socket.emit('v2:model-asset:updateEventData', data); } + // const handleActionUpdate = useCallback((updatedAction: Partial) => { + // if (!selectedActionSphere?.points?.uuid) return; + + // const updatedPaths = simulationStates.map((path) => { + // return path; + // }); + + // const updatedPath = updatedPaths.find( + // (path): path is Types.ArmBotEventsSchema => + // path.type === "ArmBot" && + // path.points.uuid === selectedActionSphere.points.uuid + // ); + // updateBackend(updatedPath); + + // setSimulationStates(updatedPaths); + // }, [selectedActionSphere?.points?.uuid, simulationStates, setSimulationStates]); + + // const handleSpeedChange = useCallback((speed: number) => { + // handleActionUpdate({ speed }); + // }, [handleActionUpdate]); + + // const handleProcessChange = useCallback((processes: Types.ArmBotEventsSchema['points']['actions']['processes']) => { + // handleActionUpdate({ processes }); + // }, [handleActionUpdate]); + + // const handleTriggerSelect = useCallback((displayName: string) => { + // const selected = connectedTriggers.find(t => t.displayName === displayName); + // setSelectedTrigger(selected?.uuid || null); + // }, [connectedTriggers]); + + // const handleStartPointSelect = useCallback((pointUUID: string) => { + // if (!selectedTrigger || !selectedPoint) return; + + // const updatedProcesses = selectedPoint.actions.processes?.map(process => + // process.triggerId === selectedTrigger + // ? { ...process, startPoint: pointUUID } + // : process + // ) || []; + + // handleProcessChange(updatedProcesses); + // }, [selectedTrigger, selectedPoint, handleProcessChange]); + + // const handleEndPointSelect = useCallback((pointUUID: string) => { + // if (!selectedTrigger || !selectedPoint) return; + + // const updatedProcesses = selectedPoint.actions.processes?.map(process => + // process.triggerId === selectedTrigger + // ? { ...process, endPoint: pointUUID } + // : process + // ) || []; + + // handleProcessChange(updatedProcesses); + // }, [selectedTrigger, selectedPoint, handleProcessChange]); + + // const getCurrentProcess = useCallback(() => { + // if (!selectedTrigger || !selectedPoint) return null; + // return selectedPoint.actions.processes?.find(p => p.triggerId === selectedTrigger); + // }, [selectedTrigger, selectedPoint]); return (
@@ -71,16 +127,50 @@ const ArmBotMechanics: React.FC = () => {
ArmBot Properties
- {selectedPoint && ( + {/* {selectedPoint && ( <> + handleSpeedChange(parseInt(value))} + /> + t.uuid === selectedTrigger)?.displayName || ''} + onSelect={handleTriggerSelect} + options={connectedTriggers.map(trigger => trigger.displayName)} + /> + + {selectedTrigger && ( + <> + `${model.modelName} [${index}]`)} + /> + + `${model.modelName} [${index}]`)} + /> + + + )} - )} + )} */}
- Configure armbot properties. + Configure ArmBot properties and trigger-based processes.
diff --git a/app/src/components/layout/sidebarRight/mechanics/StaticMachineMechanics.tsx b/app/src/components/layout/sidebarRight/mechanics/StaticMachineMechanics.tsx index 254753e..564f221 100644 --- a/app/src/components/layout/sidebarRight/mechanics/StaticMachineMechanics.tsx +++ b/app/src/components/layout/sidebarRight/mechanics/StaticMachineMechanics.tsx @@ -1,9 +1,9 @@ -import React, { useRef, useMemo } from "react"; +import React, { useRef, useMemo, useCallback } from "react"; import { InfoIcon } from "../../../icons/ExportCommonIcons"; import InputWithDropDown from "../../../ui/inputs/InputWithDropDown"; -import { useEditingPoint, useEyeDropMode, usePreviewPosition, useSelectedActionSphere, useSimulationStates, useSocketStore } from "../../../../store/store"; +import { useSelectedActionSphere, useSimulationStates, useSocketStore } from "../../../../store/store"; import * as Types from '../../../../types/world/worldTypes'; -import PositionInput from "../customInput/PositionInputs"; +import LabledDropdown from "../../../ui/inputs/LabledDropdown"; import { setEventApi } from "../../../../services/factoryBuilder/assest/floorAsset/setEventsApt"; const StaticMachineMechanics: React.FC = () => { @@ -16,11 +16,11 @@ const StaticMachineMechanics: React.FC = () => { const { selectedPoint, connectedPointUuids } = useMemo(() => { if (!selectedActionSphere?.points?.uuid) return { selectedPoint: null, connectedPointUuids: [] }; - const vehiclePaths = simulationStates.filter( + const staticMachinePaths = simulationStates.filter( (path): path is Types.StaticMachineEventsSchema => path.type === "StaticMachine" ); - const points = vehiclePaths.find( + const points = staticMachinePaths.find( (path) => path.points.uuid === selectedActionSphere.points.uuid )?.points; @@ -47,7 +47,7 @@ const StaticMachineMechanics: React.FC = () => { // await setEventApi( // organization, // updatedPath.modeluuid, - // { type: "StaticMachine", points: updatedPath.points } + // { type: "Vehicle", points: updatedPath.points } // ); const data = { @@ -57,9 +57,77 @@ const StaticMachineMechanics: React.FC = () => { } socket.emit('v2:model-asset:updateEventData', data); - } + const handleActionUpdate = useCallback((updatedAction: Partial) => { + if (!selectedActionSphere?.points?.uuid) return; + + const updatedPaths = simulationStates.map((path) => { + if (path.type === "StaticMachine" && path.points.uuid === selectedActionSphere.points.uuid) { + return { + ...path, + points: { + ...path.points, + actions: { + ...path.points.actions, + ...updatedAction + } + } + }; + } + return path; + }); + + const updatedPath = updatedPaths.find( + (path): path is Types.StaticMachineEventsSchema => + path.type === "StaticMachine" && + path.points.uuid === selectedActionSphere.points.uuid + ); + updateBackend(updatedPath); + + setSimulationStates(updatedPaths); + }, [selectedActionSphere?.points?.uuid, simulationStates, setSimulationStates]); + + const handleBufferChange = useCallback((buffer: number) => { + handleActionUpdate({ buffer }); + }, [handleActionUpdate]); + + const handleMaterialChange = useCallback((material: string) => { + handleActionUpdate({ material }); + }, [handleActionUpdate]); + + const handleTriggerChange = useCallback((updatedTrigger: Partial) => { + if (!selectedActionSphere?.points?.uuid) return; + + const updatedPaths = simulationStates.map((path) => { + if (path.type === "StaticMachine" && path.points.uuid === selectedActionSphere.points.uuid) { + return { + ...path, + points: { + ...path.points, + triggers: { + ...path.points.triggers, + ...updatedTrigger + } + } + }; + } + return path; + }); + + const updatedPath = updatedPaths.find( + (path): path is Types.StaticMachineEventsSchema => + path.type === "StaticMachine" && + path.points.uuid === selectedActionSphere.points.uuid + ); + updateBackend(updatedPath); + + setSimulationStates(updatedPaths); + }, [selectedActionSphere?.points?.uuid, simulationStates, setSimulationStates]); + + const handleTriggerTypeChange = useCallback((type: string) => { + handleTriggerChange({ type }); + }, [handleTriggerChange]); return (
@@ -67,20 +135,50 @@ const StaticMachineMechanics: React.FC = () => { {selectedActionSphere?.path?.modelName || "Machine point not found"}
+
Machine Properties
{selectedPoint && ( <> + handleBufferChange(parseInt(value))} + /> + handleMaterialChange(value)} + options={["Inherit", "Crate", "Box"]} + /> + + handleTriggerTypeChange(value)} + options={["OnComplete", "OnStart"]} + /> + + {/* { + // Implement reset functionality if needed + }} + /> */} )}
- Configure machine properties. + Configure machine interaction properties and triggers.
diff --git a/app/src/components/layout/sidebarRight/mechanics/VehicleMechanics.tsx b/app/src/components/layout/sidebarRight/mechanics/VehicleMechanics.tsx index 4db77fe..67d132f 100644 --- a/app/src/components/layout/sidebarRight/mechanics/VehicleMechanics.tsx +++ b/app/src/components/layout/sidebarRight/mechanics/VehicleMechanics.tsx @@ -5,6 +5,7 @@ import { useEditingPoint, useEyeDropMode, usePreviewPosition, useSelectedActionS import * as Types from '../../../../types/world/worldTypes'; import PositionInput from "../customInput/PositionInputs"; import { setEventApi } from "../../../../services/factoryBuilder/assest/floorAsset/setEventsApt"; +import LabeledButton from "../../../ui/inputs/LabledButton"; const VehicleMechanics: React.FC = () => { const { selectedActionSphere } = useSelectedActionSphere(); @@ -126,6 +127,33 @@ const VehicleMechanics: React.FC = () => { setSimulationStates(updatedPaths); }, [selectedActionSphere?.points?.uuid, simulationStates, setSimulationStates]); + + const ResetVehicleState = React.useCallback(() => { + if (!selectedActionSphere?.points?.uuid) return; + + const updatedPaths = simulationStates.map((state) => { + if (state.type === "Vehicle" && state.points.uuid === selectedActionSphere.points.uuid) { + return { + ...state, + points: { + ...state.points, + actions: { ...state.points.actions, start: {}, end: {} } + } + }; + } + return state; + }); + + const updatedPath = updatedPaths.find( + (path): path is Types.VehicleEventsSchema => + path.type === "Vehicle" && + path.points.uuid === selectedActionSphere.points.uuid + ); + updateBackend(updatedPath); + + setSimulationStates(updatedPaths); + }, [selectedActionSphere?.points?.uuid, simulationStates, setSimulationStates]); + const handleStartEyeDropClick = () => { setEditingPoint('start'); setEyeDropMode(true); @@ -193,6 +221,14 @@ const VehicleMechanics: React.FC = () => { handleEyeDropClick={handleEndEyeDropClick} /> + { + ResetVehicleState(); + }} + /> + { const [userData, setUserData] = useState([]); // State to track user data const [nextId, setNextId] = useState(1); // Unique ID for new entries - const { selectedFloorItem } = useselectedFloorItem(); + const { selectedFloorItem } = useSelectedFloorItem(); // Function to handle adding new user data const handleAddUserData = () => { const newUserData: UserData = { diff --git a/app/src/components/ui/Tools.tsx b/app/src/components/ui/Tools.tsx index 3de44bf..f52006a 100644 --- a/app/src/components/ui/Tools.tsx +++ b/app/src/components/ui/Tools.tsx @@ -22,7 +22,7 @@ import { useSelectedZoneStore } from "../../store/useZoneStore"; import { useActiveTool, useAddAction, - useDeleteModels, + useDeleteTool, useDeletePointOrLine, useMovePoint, useRefTextUpdate, @@ -35,6 +35,7 @@ import { import useToggleStore from "../../store/useUIToggleStore"; import { use3DWidget, + useDroppedObjectsStore, useFloatingWidget, } from "../../store/useDroppedObjectsStore"; @@ -52,11 +53,15 @@ const Tools: React.FC = () => { const { addTemplate } = useTemplateStore(); const { selectedZone } = useSelectedZoneStore(); const { floatingWidget } = useFloatingWidget(); + const { widgets3D } = use3DWidget(); + const zones = useDroppedObjectsStore((state) => state.zones); + + // wall options const { toggleView, setToggleView } = useToggleView(); - const { setDeleteModels } = useDeleteModels(); + const { setDeleteTool } = useDeleteTool(); const { setAddAction } = useAddAction(); const { setSelectedWallItem } = useSelectedWallItem(); @@ -84,7 +89,7 @@ const Tools: React.FC = () => { const toggleSwitch = () => { if (toggleThreeD) { setSelectedWallItem(null); - setDeleteModels(false); + setDeleteTool(false); setAddAction(null); setToggleView(true); // localStorage.setItem("navBarUi", JSON.stringify(!toggleThreeD)); @@ -131,7 +136,7 @@ const Tools: React.FC = () => { useEffect(() => { setToolMode(null); - setDeleteModels(false); + setDeleteTool(false); setAddAction(null); setTransformMode(null); setMovePoint(false); @@ -197,7 +202,7 @@ const Tools: React.FC = () => { if (toggleView) { setDeletePointOrLine(true); } else { - setDeleteModels(true); + setDeleteTool(true); } break; @@ -409,10 +414,9 @@ const Tools: React.FC = () => { widgets3D, selectedZone, templates, - visualizationSocket - }) - } - } + visualizationSocket, + }); + }} >
diff --git a/app/src/components/ui/componets/DisplayZone.tsx b/app/src/components/ui/componets/DisplayZone.tsx index 0890e5c..03fd44f 100644 --- a/app/src/components/ui/componets/DisplayZone.tsx +++ b/app/src/components/ui/componets/DisplayZone.tsx @@ -74,6 +74,7 @@ const DisplayZone: React.FC = ({ const [showLeftArrow, setShowLeftArrow] = useState(false); const [showRightArrow, setShowRightArrow] = useState(false); const { floatingWidget, setFloatingWidget } = useFloatingWidget() + const{setSelectedChartId}=useWidgetStore() // Function to calculate overflow state @@ -178,7 +179,7 @@ const DisplayZone: React.FC = ({ zoneViewPortPosition: response.viewPortposition || {}, }); } catch (error) { - console.error(error); + } } diff --git a/app/src/components/ui/componets/DraggableWidget.tsx b/app/src/components/ui/componets/DraggableWidget.tsx index fbb376f..cfd002d 100644 --- a/app/src/components/ui/componets/DraggableWidget.tsx +++ b/app/src/components/ui/componets/DraggableWidget.tsx @@ -18,6 +18,7 @@ import { deleteWidgetApi } from "../../../services/realTimeVisulization/zoneData import { useClickOutside } from "./functions/handleWidgetsOuterClick"; import { useSocketStore } from "../../../store/store"; import { usePlayButtonStore } from "../../../store/usePlayButtonStore"; +import OuterClick from "../../../utils/outerClick"; type Side = "top" | "bottom" | "left" | "right"; @@ -89,6 +90,11 @@ export const DraggableWidget = ({ const isPanelHidden = hiddenPanels.includes(widget.panel); + // OuterClick({ + // contextClassName: ["chart-container", "floating", "sidebar-right-wrapper"], + // setMenuVisible: () => setSelectedChartId(null), + // }); + const deleteSelectedChart = async () => { try { const email = localStorage.getItem("email") || ""; @@ -251,36 +257,35 @@ export const DraggableWidget = ({ }); // Track canvas dimensions - + // Current: Two identical useEffect hooks for canvas dimensions + // Remove the duplicate and keep only one + useEffect(() => { + const canvas = document.getElementById("real-time-vis-canvas"); + if (!canvas) return; -// Current: Two identical useEffect hooks for canvas dimensions -// Remove the duplicate and keep only one -useEffect(() => { - const canvas = document.getElementById("real-time-vis-canvas"); - if (!canvas) return; + const updateCanvasDimensions = () => { + const rect = canvas.getBoundingClientRect(); + setCanvasDimensions({ + width: rect.width, + height: rect.height, + }); + }; - const updateCanvasDimensions = () => { - const rect = canvas.getBoundingClientRect(); - setCanvasDimensions({ - width: rect.width, - height: rect.height, - }); - }; + updateCanvasDimensions(); + const resizeObserver = new ResizeObserver(updateCanvasDimensions); + resizeObserver.observe(canvas); - updateCanvasDimensions(); - const resizeObserver = new ResizeObserver(updateCanvasDimensions); - resizeObserver.observe(canvas); - - return () => resizeObserver.unobserve(canvas); -}, []); + return () => resizeObserver.unobserve(canvas); + }, []); return ( <> @@ -296,13 +301,12 @@ useEffect(() => { onDragOver={handleDragOver} onDrop={handleDrop} style={{ - // Apply styles based on panel position width: ["top", "bottom"].includes(widget.panel) - ? `calc(${canvasDimensions.width * 0.16}px - 2px)` // For top/bottom panels, set width - : undefined, // Don't set width if it's left or right + ? `calc(${canvasDimensions.width}px / 6)` + : undefined, height: ["left", "right"].includes(widget.panel) - ? `calc(${canvasDimensions.height * 0.25}px - 2px)` // For left/right panels, set height - : undefined, // Don't set height if it's top or bottom + ? `calc(${canvasDimensions.height - 10}px / 4)` + : undefined, }} ref={chartWidget} onClick={() => setSelectedChartId(widget)} @@ -393,4 +397,4 @@ useEffect(() => { ); }; -// while resize calculate --realTimeViz-container-height pprperly \ No newline at end of file + diff --git a/app/src/components/ui/componets/Dropped3dWidget.tsx b/app/src/components/ui/componets/Dropped3dWidget.tsx index f03dea8..5dbdb5b 100644 --- a/app/src/components/ui/componets/Dropped3dWidget.tsx +++ b/app/src/components/ui/componets/Dropped3dWidget.tsx @@ -1,6 +1,10 @@ import { useThree } from "@react-three/fiber"; import React, { useEffect, useRef } from "react"; -import { useAsset3dWidget, useSocketStore, useWidgetSubOption } from "../../../store/store"; +import { + useAsset3dWidget, + useSocketStore, + useWidgetSubOption, +} from "../../../store/store"; import useModuleStore from "../../../store/useModuleStore"; import { ThreeState } from "../../../types/world/worldTypes"; import * as THREE from "three"; @@ -13,11 +17,19 @@ import { generateUniqueId } from "../../../functions/generateUniqueId"; import { adding3dWidgets } from "../../../services/realTimeVisulization/zoneData/add3dWidget"; import { get3dWidgetZoneData } from "../../../services/realTimeVisulization/zoneData/get3dWidgetData"; import { use3DWidget } from "../../../store/useDroppedObjectsStore"; -import { useEditWidgetOptionsStore, useLeftData, useRightClickSelected, useRightSelected, useTopData, useZoneWidgetStore } from "../../../store/useZone3DWidgetStore"; -import { useWidgetStore } from "../../../store/useWidgetStore"; -import EditWidgetOption from "../menu/EditWidgetOption"; +import { + useEditWidgetOptionsStore, + useLeftData, + useRightClickSelected, + useRightSelected, + useTopData, + useZoneWidgetStore, +} from "../../../store/useZone3DWidgetStore"; import { delete3dWidgetApi } from "../../../services/realTimeVisulization/zoneData/delete3dWidget"; -import { update3dWidget, update3dWidgetRotation } from "../../../services/realTimeVisulization/zoneData/update3dWidget"; +import { + update3dWidget, + update3dWidgetRotation, +} from "../../../services/realTimeVisulization/zoneData/update3dWidget"; type WidgetData = { id: string; type: string; @@ -26,7 +38,6 @@ type WidgetData = { tempPosition?: [number, number, number]; }; - export default function Dropped3dWidgets() { const { widgetSelect } = useAsset3dWidget(); const { activeModule } = useModuleStore(); @@ -36,19 +47,18 @@ export default function Dropped3dWidgets() { const { top, setTop } = useTopData(); const { left, setLeft } = useLeftData(); const { rightSelect, setRightSelect } = useRightSelected(); - const { editWidgetOptions, setEditWidgetOptions } = useEditWidgetOptionsStore() - const { zoneWidgetData, setZoneWidgetData, addWidget, updateWidgetPosition, updateWidgetRotation } = useZoneWidgetStore(); + const { editWidgetOptions, setEditWidgetOptions } = useEditWidgetOptionsStore(); + const { zoneWidgetData, setZoneWidgetData, addWidget, updateWidgetPosition, updateWidgetRotation, tempWidget, tempWidgetPosition } = useZoneWidgetStore(); const { setWidgets3D } = use3DWidget(); const { visualizationSocket } = useSocketStore(); const { rightClickSelected, setRightClickSelected } = useRightClickSelected(); const plane = useRef(new THREE.Plane(new THREE.Vector3(0, 1, 0), 0)); // Floor plane for horizontal move const verticalPlane = useRef(new THREE.Plane(new THREE.Vector3(0, 0, 1), 0)); // Vertical plane for vertical move const planeIntersect = useRef(new THREE.Vector3()); - const rotationStartRef = useRef<[number, number, number]>([0, 0, 0]); const mouseStartRef = useRef<{ x: number; y: number }>({ x: 0, y: 0 }); - + const activeZoneWidgets = zoneWidgetData[selectedZone.zoneId] || []; useEffect(() => { if (activeModule !== "visualization") return; if (!selectedZone.zoneId) return; @@ -57,7 +67,10 @@ export default function Dropped3dWidgets() { const organization = email?.split("@")[1]?.split(".")[0]; async function get3dWidgetData() { - const result = await get3dWidgetZoneData(selectedZone.zoneId, organization); + const result = await get3dWidgetZoneData( + selectedZone.zoneId, + organization + ); setWidgets3D(result); @@ -68,66 +81,50 @@ export default function Dropped3dWidgets() { rotation: widget.rotation || [0, 0, 0], })); - setZoneWidgetData(selectedZone.zoneId, formattedWidgets); } get3dWidgetData(); }, [selectedZone.zoneId, activeModule]); + const createdWidgetRef = useRef(null); + useEffect(() => { if (activeModule !== "visualization") return; if (widgetSubOption === "Floating" || widgetSubOption === "2D") return; if (selectedZone.zoneName === "") return; - const canvasElement = gl.domElement; + const canvasElement = document.getElementById("real-time-vis-canvas"); + + if (!canvasElement) return; + + const hasEntered = { current: false }; const handleDragEnter = (event: DragEvent) => { event.preventDefault(); event.stopPropagation(); - console.log("Drag enter"); - }; + if (hasEntered.current || !widgetSelect.startsWith("ui")) return; + hasEntered.current = true; - const handleDragOver = (event: DragEvent) => { - event.preventDefault(); - event.stopPropagation(); - - }; - - const handleDragLeave = (event: DragEvent) => { - event.preventDefault(); - event.stopPropagation(); - console.log("Drag leave"); - // Remove visual feedback - canvasElement.style.cursor = ""; - }; - - const onDrop = async (event: DragEvent) => { - event.preventDefault(); - event.stopPropagation(); - canvasElement.style.cursor = ""; // Reset cursor - - const email = localStorage.getItem("email") || ""; - const organization = email?.split("@")[1]?.split(".")[0]; - if (!widgetSelect.startsWith("ui")) return; const group1 = scene.getObjectByName("itemsGroup"); if (!group1) return; - // Update raycaster with current mouse position const rect = canvasElement.getBoundingClientRect(); mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1; mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1; raycaster.setFromCamera(mouse, camera); - const intersects = raycaster.intersectObjects(scene.children, true).filter( - (intersect) => - !intersect.object.name.includes("Roof") && - !intersect.object.name.includes("agv-collider") && - !intersect.object.name.includes("MeasurementReference") && - !intersect.object.userData.isPathObject && - !(intersect.object.type === "GridHelper") - ); + const intersects = raycaster + .intersectObjects(scene.children, true) + .filter( + (intersect) => + !intersect.object.name.includes("Roof") && + !intersect.object.name.includes("agv-collider") && + !intersect.object.name.includes("MeasurementReference") && + !intersect.object.userData.isPathObject && + !(intersect.object.type === "GridHelper") + ); if (intersects.length > 0) { const { x, y, z } = intersects[0].point; @@ -138,36 +135,100 @@ export default function Dropped3dWidgets() { rotation: [0, 0, 0], }; - const add3dWidget = { - organization: organization, - widget: newWidget, - zoneId: selectedZone.zoneId - }; - - if (visualizationSocket) { - visualizationSocket.emit("v2:viz-3D-widget:add", add3dWidget); - } - - addWidget(selectedZone.zoneId, newWidget); + createdWidgetRef.current = newWidget; + tempWidget(selectedZone.zoneId, newWidget); // temp add in UI } }; - // Add all event listeners - // canvasElement.addEventListener("dragenter", handleDragEnter); - // canvasElement.addEventListener("dragover", handleDragOver); - // canvasElement.addEventListener("dragleave", handleDragLeave); + const handleDragOver = (event: DragEvent) => { + event.preventDefault(); + event.stopPropagation(); + event.dataTransfer!.dropEffect = "move"; // ✅ Add this line + const widget = createdWidgetRef.current; + if (!widget) return; + + const rect = canvasElement.getBoundingClientRect(); + mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1; + mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1; + raycaster.setFromCamera(mouse, camera); + + const intersects = raycaster + .intersectObjects(scene.children, true) + .filter( + (intersect) => + !intersect.object.name.includes("Roof") && + !intersect.object.name.includes("agv-collider") && + !intersect.object.name.includes("MeasurementReference") && + !intersect.object.userData.isPathObject && + !(intersect.object.type === "GridHelper") + ); + // Update widget's position in memory + if (intersects.length > 0) { + const { x, y, z } = intersects[0].point; + tempWidgetPosition(selectedZone.zoneId, widget.id, [x, y, z]); + widget.position = [x, y, z]; + } + + }; + + const onDrop = (event: any) => { + console.log("onDrop called. hasEntered: ", hasEntered.current); + event.preventDefault(); + event.stopPropagation(); + + hasEntered.current = false; + + const email = localStorage.getItem("email") || ""; + const organization = email?.split("@")[1]?.split(".")[0]; + + const newWidget = createdWidgetRef.current; + if (!newWidget || !widgetSelect.startsWith("ui")) return; + + // ✅ Manual removal of the temp widget (same ID) + const prevWidgets = useZoneWidgetStore.getState().zoneWidgetData[selectedZone.zoneId] || []; + const cleanedWidgets = prevWidgets.filter(w => w.id !== newWidget.id); + useZoneWidgetStore.setState((state) => ({ + zoneWidgetData: { + ...state.zoneWidgetData, + [selectedZone.zoneId]: cleanedWidgets, + }, + })); + + // ✅ Now re-add it as final + addWidget(selectedZone.zoneId, newWidget); + + const add3dWidget = { + organization, + widget: newWidget, + zoneId: selectedZone.zoneId, + }; + + if (visualizationSocket) { + visualizationSocket.emit("v2:viz-3D-widget:add", add3dWidget); + } + + setTimeout(() => { + let pointerDivs = document.getElementsByClassName("pointer-none"); + Array.from(pointerDivs).forEach((el) => { + el.classList.remove("pointer-none"); + }); + }, 1000); + + createdWidgetRef.current = null; + }; + + canvasElement.addEventListener("dragenter", handleDragEnter); + canvasElement.addEventListener("dragover", handleDragOver); canvasElement.addEventListener("drop", onDrop); return () => { - // // Clean up all event listeners - // canvasElement.removeEventListener("dragenter", handleDragEnter); - // canvasElement.removeEventListener("dragover", handleDragOver); - // canvasElement.removeEventListener("dragleave", handleDragLeave); + canvasElement.removeEventListener("dragenter", handleDragEnter); + canvasElement.removeEventListener("dragover", handleDragOver); canvasElement.removeEventListener("drop", onDrop); - canvasElement.style.cursor = ""; // Ensure cursor is reset }; - }, [widgetSelect, activeModule, selectedZone.zoneId, widgetSubOption, gl.domElement, scene, raycaster, camera]); - const activeZoneWidgets = zoneWidgetData[selectedZone.zoneId] || []; + }, [widgetSelect, activeModule, selectedZone.zoneId, widgetSubOption, camera,]); + + useEffect(() => { if (!rightClickSelected) return; @@ -176,7 +237,9 @@ export default function Dropped3dWidgets() { if (rightSelect === "Duplicate") { async function duplicateWidget() { - const widgetToDuplicate = activeZoneWidgets.find((w: WidgetData) => w.id === rightClickSelected); + const widgetToDuplicate = activeZoneWidgets.find( + (w: WidgetData) => w.id === rightClickSelected + ); if (!widgetToDuplicate) return; const newWidget: WidgetData = { id: generateUniqueId(), @@ -191,19 +254,19 @@ export default function Dropped3dWidgets() { const adding3dWidget = { organization: organization, widget: newWidget, - zoneId: selectedZone.zoneId + zoneId: selectedZone.zoneId, }; if (visualizationSocket) { visualizationSocket.emit("v2:viz-3D-widget:add", adding3dWidget); } // let response = await adding3dWidgets(selectedZone.zoneId, organization, newWidget) - // + // addWidget(selectedZone.zoneId, newWidget); setRightSelect(null); setRightClickSelected(null); } - duplicateWidget() + duplicateWidget(); } if (rightSelect === "Delete") { @@ -215,7 +278,6 @@ export default function Dropped3dWidgets() { zoneId: selectedZone.zoneId, }; - if (visualizationSocket) { visualizationSocket.emit("v2:viz-3D-widget:delete", deleteWidget); } @@ -223,10 +285,11 @@ export default function Dropped3dWidgets() { // const response = await delete3dWidgetApi(selectedZone.zoneId, organization, rightClickSelected); setZoneWidgetData( selectedZone.zoneId, - activeZoneWidgets.filter((w: WidgetData) => w.id !== rightClickSelected) + activeZoneWidgets.filter( + (w: WidgetData) => w.id !== rightClickSelected + ) ); } catch (error) { - } finally { setRightClickSelected(null); setRightSelect(null); @@ -238,7 +301,6 @@ export default function Dropped3dWidgets() { }, [rightSelect, rightClickSelected]); useEffect(() => { - const email = localStorage.getItem("email") || ""; const organization = email?.split("@")[1]?.split(".")[0]; const handleMouseDown = (event: MouseEvent) => { @@ -247,13 +309,18 @@ export default function Dropped3dWidgets() { if (rightSelect === "RotateX" || rightSelect === "RotateY") { mouseStartRef.current = { x: event.clientX, y: event.clientY }; - const selectedZone = Object.keys(zoneWidgetData).find((zoneId: string) => - zoneWidgetData[zoneId].some((widget: WidgetData) => widget.id === rightClickSelected) + const selectedZone = Object.keys(zoneWidgetData).find( + (zoneId: string) => + zoneWidgetData[zoneId].some( + (widget: WidgetData) => widget.id === rightClickSelected + ) ); if (!selectedZone) return; - const selectedWidget = zoneWidgetData[selectedZone].find((widget: WidgetData) => widget.id === rightClickSelected); + const selectedWidget = zoneWidgetData[selectedZone].find( + (widget: WidgetData) => widget.id === rightClickSelected + ); if (selectedWidget) { rotationStartRef.current = selectedWidget.rotation || [0, 0, 0]; } @@ -263,11 +330,15 @@ export default function Dropped3dWidgets() { const handleMouseMove = (event: MouseEvent) => { if (!rightClickSelected || !rightSelect) return; const selectedZone = Object.keys(zoneWidgetData).find((zoneId: string) => - zoneWidgetData[zoneId].some((widget: WidgetData) => widget.id === rightClickSelected) + zoneWidgetData[zoneId].some( + (widget: WidgetData) => widget.id === rightClickSelected + ) ); if (!selectedZone) return; - const selectedWidget = zoneWidgetData[selectedZone].find((widget: WidgetData) => widget.id === rightClickSelected); + const selectedWidget = zoneWidgetData[selectedZone].find( + (widget: WidgetData) => widget.id === rightClickSelected + ); if (!selectedWidget) return; const rect = gl.domElement.getBoundingClientRect(); @@ -276,22 +347,29 @@ export default function Dropped3dWidgets() { raycaster.setFromCamera(mouse, camera); - if (rightSelect === "Horizontal Move" && raycaster.ray.intersectPlane(plane.current, planeIntersect.current)) { + if ( + rightSelect === "Horizontal Move" && + raycaster.ray.intersectPlane(plane.current, planeIntersect.current) + ) { const newPosition: [number, number, number] = [ planeIntersect.current.x, selectedWidget.position[1], - planeIntersect.current.z + planeIntersect.current.z, ]; updateWidgetPosition(selectedZone, rightClickSelected, newPosition); - } if (rightSelect === "Vertical Move") { - if (raycaster.ray.intersectPlane(verticalPlane.current, planeIntersect.current)) { + if ( + raycaster.ray.intersectPlane( + verticalPlane.current, + planeIntersect.current + ) + ) { updateWidgetPosition(selectedZone, rightClickSelected, [ selectedWidget.position[0], planeIntersect.current.y, - selectedWidget.position[2] + selectedWidget.position[2], ]); } } @@ -302,7 +380,7 @@ export default function Dropped3dWidgets() { const newRotation: [number, number, number] = [ rotationStartRef.current[0] + deltaX * rotationSpeed, rotationStartRef.current[1], - rotationStartRef.current[2] + rotationStartRef.current[2], ]; updateWidgetRotation(selectedZone, rightClickSelected, newRotation); } @@ -313,7 +391,7 @@ export default function Dropped3dWidgets() { const newRotation: [number, number, number] = [ rotationStartRef.current[0], rotationStartRef.current[1] + deltaY * rotationSpeed, - rotationStartRef.current[2] + rotationStartRef.current[2], ]; updateWidgetRotation(selectedZone, rightClickSelected, newRotation); } @@ -324,31 +402,42 @@ export default function Dropped3dWidgets() { const newRotation: [number, number, number] = [ currentRotation[0], currentRotation[1], - currentRotation[2] + deltaX * rotationSpeed + currentRotation[2] + deltaX * rotationSpeed, ]; updateWidgetRotation(selectedZone, rightClickSelected, newRotation); } - }; const handleMouseUp = () => { if (!rightClickSelected || !rightSelect) return; - const selectedZone = Object.keys(zoneWidgetData).find(zoneId => - zoneWidgetData[zoneId].some(widget => widget.id === rightClickSelected) + const selectedZone = Object.keys(zoneWidgetData).find((zoneId) => + zoneWidgetData[zoneId].some( + (widget) => widget.id === rightClickSelected + ) ); if (!selectedZone) return; - const selectedWidget = zoneWidgetData[selectedZone].find(widget => widget.id === rightClickSelected); + const selectedWidget = zoneWidgetData[selectedZone].find( + (widget) => widget.id === rightClickSelected + ); if (!selectedWidget) return; // Format values to 2 decimal places - const formatValues = (vals: number[]) => vals.map(val => parseFloat(val.toFixed(2))); - if (rightSelect === "Horizontal Move" || rightSelect === "Vertical Move") { - let lastPosition = formatValues(selectedWidget.position) as [number, number, number]; + const formatValues = (vals: number[]) => + vals.map((val) => parseFloat(val.toFixed(2))); + if ( + rightSelect === "Horizontal Move" || + rightSelect === "Vertical Move" + ) { + let lastPosition = formatValues(selectedWidget.position) as [ + number, + number, + number + ]; // (async () => { // let response = await update3dWidget(selectedZone, organization, rightClickSelected, lastPosition); - // + // // if (response) { - // + // // } // })(); let updatingPosition = { @@ -356,21 +445,22 @@ export default function Dropped3dWidgets() { zoneId: selectedZone, id: rightClickSelected, position: lastPosition, - } + }; if (visualizationSocket) { - visualizationSocket.emit("v2:viz-3D-widget:modifyPositionRotation", updatingPosition); + visualizationSocket.emit( + "v2:viz-3D-widget:modifyPositionRotation", + updatingPosition + ); } - - } - else if (rightSelect.includes("Rotate")) { + } else if (rightSelect.includes("Rotate")) { const rotation = selectedWidget.rotation || [0, 0, 0]; let lastRotation = formatValues(rotation) as [number, number, number]; // (async () => { // let response = await update3dWidgetRotation(selectedZone, organization, rightClickSelected, lastRotation); - // + // // if (response) { - // + // // } // })(); let updatingRotation = { @@ -378,9 +468,12 @@ export default function Dropped3dWidgets() { zoneId: selectedZone, id: rightClickSelected, rotation: lastRotation, - } + }; if (visualizationSocket) { - visualizationSocket.emit("v2:viz-3D-widget:modifyPositionRotation", updatingRotation); + visualizationSocket.emit( + "v2:viz-3D-widget:modifyPositionRotation", + updatingRotation + ); } } @@ -403,33 +496,73 @@ export default function Dropped3dWidgets() { return ( <> - {activeZoneWidgets.map(({ id, type, position, rotation = [0, 0, 0] }: WidgetData) => { - const handleRightClick = (event: React.MouseEvent, id: string) => { - event.preventDefault(); - const canvasElement = document.getElementById("real-time-vis-canvas"); - if (!canvasElement) throw new Error("Canvas element not found"); - const canvasRect = canvasElement.getBoundingClientRect(); - const relativeX = event.clientX - canvasRect.left; - const relativeY = event.clientY - canvasRect.top; - setEditWidgetOptions(true); - setRightClickSelected(id); - setTop(relativeY); - setLeft(relativeX); - }; + {activeZoneWidgets.map( + ({ id, type, position, rotation = [0, 0, 0] }: WidgetData) => { + const handleRightClick = (event: React.MouseEvent, id: string) => { + event.preventDefault(); + const canvasElement = document.getElementById( + "real-time-vis-canvas" + ); + if (!canvasElement) throw new Error("Canvas element not found"); + const canvasRect = canvasElement.getBoundingClientRect(); + const relativeX = event.clientX - canvasRect.left; + const relativeY = event.clientY - canvasRect.top; + setEditWidgetOptions(true); + setRightClickSelected(id); + setTop(relativeY); + setLeft(relativeX); + }; - switch (type) { - case "ui-Widget 1": - return ( handleRightClick(e, id)} />); - case "ui-Widget 2": - return ( handleRightClick(e, id)} />); - case "ui-Widget 3": - return ( handleRightClick(e, id)} />); - case "ui-Widget 4": - return ( handleRightClick(e, id)} />); - default: - return null; + switch (type) { + case "ui-Widget 1": + return ( + handleRightClick(e, id)} + /> + ); + case "ui-Widget 2": + return ( + handleRightClick(e, id)} + /> + ); + case "ui-Widget 3": + return ( + handleRightClick(e, id)} + /> + ); + case "ui-Widget 4": + return ( + handleRightClick(e, id)} + /> + ); + default: + return null; + } } - })} + )} ); -} \ No newline at end of file +} diff --git a/app/src/components/ui/componets/DroppedFloatingWidgets.tsx b/app/src/components/ui/componets/DroppedFloatingWidgets.tsx index 4652ec1..bd707f4 100644 --- a/app/src/components/ui/componets/DroppedFloatingWidgets.tsx +++ b/app/src/components/ui/componets/DroppedFloatingWidgets.tsx @@ -126,12 +126,11 @@ const DroppedObjects: React.FC = () => { const email = localStorage.getItem("email") || ""; const organization = email?.split("@")[1]?.split(".")[0]; - let deleteFloatingWidget = { floatWidgetID: id, organization: organization, - zoneId: zone.zoneId - } + zoneId: zone.zoneId, + }; if (visualizationSocket) { visualizationSocket.emit("v2:viz-float:delete", deleteFloatingWidget); @@ -144,9 +143,7 @@ const DroppedObjects: React.FC = () => { // if (res.message === "FloatingWidget deleted successfully") { // deleteObject(zoneName, id, index); // Call the deleteObject method from the store // } - } catch (error) { - - } + } catch (error) {} } const handlePointerDown = (event: React.PointerEvent, index: number) => { @@ -461,15 +458,14 @@ const DroppedObjects: React.FC = () => { position: boundedPosition, }, index: draggingIndex.index, - zoneId: zone.zoneId - } + zoneId: zone.zoneId, + }; if (visualizationSocket) { visualizationSocket.emit("v2:viz-float:add", updateFloatingWidget); } // if (response.message === "Widget updated successfully") { - console.log('boundedPosition: ', boundedPosition); updateObjectPosition(zoneName, draggingIndex.index, boundedPosition); // } @@ -503,106 +499,130 @@ const DroppedObjects: React.FC = () => { setOpenKebabId((prevId) => (prevId === id ? null : id)); }; + const containerHeight = getComputedStyle( + document.documentElement + ).getPropertyValue("--realTimeViz-container-height"); + const containerWidth = getComputedStyle( + document.documentElement + ).getPropertyValue("--realTimeViz-container-width"); + + const heightMultiplier = parseFloat(containerHeight) * 0.14; + + const widthMultiplier = parseFloat(containerWidth) * 0.13; + + console.log("zone.objects: ", zone.objects); return (
- {zone.objects.map((obj, index) => ( -
{ - setSelectedChartId(obj); - handlePointerDown(event, index); - }} - > - {obj.className === "floating total-card" ? ( - <> - - - ) : obj.className === "warehouseThroughput floating" ? ( - <> - - - ) : obj.className === "fleetEfficiency floating" ? ( - <> - - - ) : null} + {zone.objects.map((obj, index) => { + const topPosition = + typeof obj.position.top === "number" + ? `calc(${obj.position.top}px + ${ + isPlaying && selectedZone.activeSides.includes("top") + ? `${heightMultiplier - 55}px` + : "0px" + })` + : "auto"; + + const leftPosition = + typeof obj.position.left === "number" + ? `calc(${obj.position.left}px + ${ + isPlaying && selectedZone.activeSides.includes("left") + ? `${widthMultiplier - 100}px` + : "0px" + })` + : "auto"; + + const rightPosition = + typeof obj.position.right === "number" + ? `calc(${obj.position.right}px + ${ + isPlaying && selectedZone.activeSides.includes("right") + ? `${widthMultiplier - 100}px` + : "0px" + })` + : "auto"; + + const bottomPosition = + typeof obj.position.bottom === "number" + ? `calc(${obj.position.bottom}px + ${ + isPlaying && selectedZone.activeSides.includes("bottom") + ? `${heightMultiplier - 55}px` + : "0px" + })` + : "auto"; + + return (
{ - event.stopPropagation(); - handleKebabClick(obj.id, event); + key={`${zoneName}-${index}`} + className={`${obj.className} ${ + selectedChartId?.id === obj.id && "activeChart" + }`} + ref={chartWidget} + style={{ + position: "absolute", + top: topPosition, + left: leftPosition, + right: rightPosition, + bottom: bottomPosition, + }} + onPointerDown={(event) => { + setSelectedChartId(obj); + handlePointerDown(event, index); }} > - -
- {openKebabId === obj.id && ( -
-
{ - event.stopPropagation(); - handleDuplicate(zoneName, index); // Call the duplicate handler - }} - > -
- -
-
Duplicate
-
-
{ - event.stopPropagation(); - handleDelete(zoneName, obj.id); // Call the delete handler - }} - > -
- -
-
Delete
-
+ {obj.className === "floating total-card" ? ( + + ) : obj.className === "warehouseThroughput floating" ? ( + + ) : obj.className === "fleetEfficiency floating" ? ( + + ) : null} + +
{ + event.stopPropagation(); + handleKebabClick(obj.id, event); + }} + > +
- )} -
- ))} + + {openKebabId === obj.id && ( +
+
{ + event.stopPropagation(); + handleDuplicate(zoneName, index); // Call the duplicate handler + }} + > +
+ +
+
Duplicate
+
+
{ + event.stopPropagation(); + handleDelete(zoneName, obj.id); // Call the delete handler + }} + > +
+ +
+
Delete
+
+
+ )} +
+ ); + })} {/* Render DistanceLines component during drag */} {isPlaying === false && diff --git a/app/src/components/ui/componets/Panel.tsx b/app/src/components/ui/componets/Panel.tsx index d24abcb..fe9836d 100644 --- a/app/src/components/ui/componets/Panel.tsx +++ b/app/src/components/ui/componets/Panel.tsx @@ -32,7 +32,6 @@ interface PanelProps { zoneName: string; activeSides: Side[]; panelOrder: Side[]; - lockedPanels: Side[]; zoneId: string; zoneViewPortTarget: number[]; @@ -41,7 +40,7 @@ interface PanelProps { }> >; hiddenPanels: string[]; - setZonesData: React.Dispatch>; // Add this line + setZonesData: React.Dispatch>; } const generateUniqueId = () => @@ -59,14 +58,13 @@ const Panel: React.FC = ({ [side in Side]?: { width: number; height: number }; }>({}); const [openKebabId, setOpenKebabId] = useState(null); - const { isPlaying } = usePlayButtonStore(); const { visualizationSocket } = useSocketStore(); - const [canvasDimensions, setCanvasDimensions] = useState({ width: 0, height: 0, }); + // Track canvas dimensions useEffect(() => { const canvas = document.getElementById("real-time-vis-canvas"); @@ -80,42 +78,20 @@ const Panel: React.FC = ({ }); }; - // Initial measurement updateCanvasDimensions(); - - // Set up ResizeObserver to track changes const resizeObserver = new ResizeObserver(updateCanvasDimensions); resizeObserver.observe(canvas); - return () => { - resizeObserver.unobserve(canvas); - }; + return () => resizeObserver.unobserve(canvas); }, []); - useEffect(() => { - const canvas = document.getElementById("real-time-vis-canvas"); - if (!canvas) return; - - const updateCanvasDimensions = () => { - const rect = canvas.getBoundingClientRect(); - setCanvasDimensions({ - width: rect.width, - height: rect.height, - }); - }; - - // Initial measurement - updateCanvasDimensions(); - - // Set up ResizeObserver to track changes - const resizeObserver = new ResizeObserver(updateCanvasDimensions); - resizeObserver.observe(canvas); - - return () => { - resizeObserver.unobserve(canvas); - }; - }, []); + // Calculate panel size + const panelSize = Math.max( + Math.min(canvasDimensions.width * 0.25, canvasDimensions.height * 0.25), + 170 // Min 170px + ); + // Define getPanelStyle const getPanelStyle = useMemo( () => (side: Side) => { const currentIndex = selectedZone.panelOrder.indexOf(side); @@ -125,123 +101,105 @@ const Panel: React.FC = ({ const topActive = previousPanels.includes("top"); const bottomActive = previousPanels.includes("bottom"); - // Dynamic panel sizes based on canvas width - const panelSizeWidth = isPlaying ? Math.max(canvasDimensions.width * 0.14, 200) : Math.max(canvasDimensions.width * 0.165, 200); // Ensure minimum width of 200px - const panelSizeHeight = isPlaying ? Math.max(canvasDimensions.width * 0.13, 200) : Math.max(canvasDimensions.width * 0.13, 200); // Ensure minimum height of 200px - switch (side) { case "top": case "bottom": return { - // minWidth: "200px", // Minimum width constraint - width: `calc(100% - ${(leftActive ? panelSizeWidth : 0) + - (rightActive ? panelSizeWidth : 0) - }px)`, - minHeight: "200px", // Minimum height constraint - height: `${panelSizeHeight - 2}px`, // Subtracting for border or margin - left: leftActive ? `${panelSizeWidth}px` : "0", - right: rightActive ? `${panelSizeWidth}px` : "0", + minWidth: "170px", + width: `calc(100% - ${ + (leftActive ? panelSize : 0) + (rightActive ? panelSize : 0) + }px)`, + minHeight: "170px", + height: `${panelSize}px`, + left: leftActive ? `${panelSize}px` : "0", + right: rightActive ? `${panelSize}px` : "0", [side]: "0", }; - case "left": case "right": return { - minWidth: "200px", // Minimum width constraint - width: `${panelSizeWidth - 2}px`, // Subtracting for border or margin - // minHeight: "200px", // Minimum height constraint - height: `calc(100% - ${(topActive ? panelSizeHeight : 0) + - (bottomActive ? panelSizeHeight : 0) - }px)`, - top: topActive ? `${panelSizeHeight}px` : "0", - bottom: bottomActive ? `${panelSizeHeight}px` : "0", + minWidth: "170px", + width: `${panelSize}px`, + minHeight: "170px", + height: `calc(100% - ${ + (topActive ? panelSize : 0) + (bottomActive ? panelSize : 0) + }px)`, + top: topActive ? `${panelSize}px` : "0", + bottom: bottomActive ? `${panelSize}px` : "0", [side]: "0", }; - default: return {}; } }, - [ - selectedZone.panelOrder, - isPlaying, - canvasDimensions.width, - canvasDimensions.height, - ] + [selectedZone.panelOrder, panelSize] ); + // Handle drop event const handleDrop = (e: React.DragEvent, panel: Side) => { e.preventDefault(); const { draggedAsset } = useWidgetStore.getState(); - if (!draggedAsset) return; - if (isPanelLocked(panel)) return; + if (!draggedAsset || isPanelLocked(panel)) return; const currentWidgetsCount = getCurrentWidgetCount(panel); const maxCapacity = calculatePanelCapacity(panel); - if (currentWidgetsCount >= maxCapacity) return; - addWidgetToPanel(draggedAsset, panel); + if (currentWidgetsCount < maxCapacity) { + addWidgetToPanel(draggedAsset, panel); + } }; + // Check if panel is locked const isPanelLocked = (panel: Side) => selectedZone.lockedPanels.includes(panel); + // Get current widget count in a panel const getCurrentWidgetCount = (panel: Side) => selectedZone.widgets.filter((w) => w.panel === panel).length; + // Calculate panel capacity const calculatePanelCapacity = (panel: Side) => { - const CHART_WIDTH = 170; - const CHART_HEIGHT = 170; - const FALLBACK_HORIZONTAL_CAPACITY = 5; - const FALLBACK_VERTICAL_CAPACITY = 3; + const CHART_WIDTH = panelSize; + const CHART_HEIGHT = panelSize; const dimensions = panelDimensions[panel]; if (!dimensions) { - return panel === "top" || panel === "bottom" - ? FALLBACK_HORIZONTAL_CAPACITY - : FALLBACK_VERTICAL_CAPACITY; + return panel === "top" || panel === "bottom" ? 5 : 3; // Fallback capacities } return panel === "top" || panel === "bottom" - ? Math.floor(dimensions.width / CHART_WIDTH) - : Math.floor(dimensions.height / CHART_HEIGHT); + ? Math.max(1, Math.floor(dimensions.width / CHART_WIDTH)) + : Math.max(1, Math.floor(dimensions.height / CHART_HEIGHT)); }; - // while dublicate check this and add + // Add widget to panel const addWidgetToPanel = async (asset: any, panel: Side) => { const email = localStorage.getItem("email") || ""; const organization = email?.split("@")[1]?.split(".")[0]; + const newWidget = { ...asset, id: generateUniqueId(), panel, }; + let addWidget = { organization: organization, zoneId: selectedZone.zoneId, widget: newWidget, }; + if (visualizationSocket) { visualizationSocket.emit("v2:viz-widget:add", addWidget); } + setSelectedZone((prev) => ({ ...prev, widgets: [...prev.widgets, newWidget], })); - - try { - // let response = await addingWidgets(selectedZone.zoneId, organization, newWidget); - // if (response.message === "Widget created successfully") { - // setSelectedZone((prev) => ({ - // ...prev, - // widgets: [...prev.widgets, newWidget], - // })); - // } - } catch (error) { - console.error("Error adding widget:", error); - } }; + // Observe panel dimensions useEffect(() => { const observers: ResizeObserver[] = []; const currentPanelRefs = panelRefs.current; @@ -258,6 +216,7 @@ const Panel: React.FC = ({ })); } }); + observer.observe(element); observers.push(observer); } @@ -268,22 +227,15 @@ const Panel: React.FC = ({ }; }, [selectedZone.activeSides]); + // Handle widget reordering const handleReorder = (fromIndex: number, toIndex: number, panel: Side) => { - if (!selectedZone) return; // Ensure selectedZone is not null - setSelectedZone((prev) => { - if (!prev) return prev; // Ensure prev is not null - - // Filter widgets for the specified panel const widgetsInPanel = prev.widgets.filter((w) => w.panel === panel); - - // Reorder widgets within the same panel const reorderedWidgets = arrayMove(widgetsInPanel, fromIndex, toIndex); - // Merge the reordered widgets back into the full list while preserving the order const updatedWidgets = prev.widgets - .filter((widget) => widget.panel !== panel) // Keep widgets from other panels - .concat(reorderedWidgets); // Add the reordered widgets for the specified panel + .filter((widget) => widget.panel !== panel) + .concat(reorderedWidgets); return { ...prev, @@ -292,13 +244,42 @@ const Panel: React.FC = ({ }); }; + // Calculate capacities and dimensions + const topWidth = getPanelStyle("top").width; + const bottomWidth = getPanelStyle("bottom").width; + const leftHeight = getPanelStyle("left").height; + const rightHeight = getPanelStyle("right").height; + + const topCapacity = calculatePanelCapacity("top"); + const bottomCapacity = calculatePanelCapacity("bottom"); + const leftCapacity = calculatePanelCapacity("left"); + const rightCapacity = calculatePanelCapacity("right"); + return ( <> + + {selectedZone.activeSides.map((side) => (
handleDrop(e, side)} onDragOver={(e) => e.preventDefault()} @@ -344,5 +325,3 @@ const Panel: React.FC = ({ }; export default Panel; - -// canvasDimensions.width as percent diff --git a/app/src/components/ui/componets/RealTimeVisulization.tsx b/app/src/components/ui/componets/RealTimeVisulization.tsx index 8044ed2..293f42e 100644 --- a/app/src/components/ui/componets/RealTimeVisulization.tsx +++ b/app/src/components/ui/componets/RealTimeVisulization.tsx @@ -28,6 +28,7 @@ import { useRightClickSelected, useRightSelected, } from "../../../store/useZone3DWidgetStore"; +import Dropped3dWidgets from "./Dropped3dWidget"; type Side = "top" | "bottom" | "left" | "right"; @@ -127,6 +128,8 @@ const RealTimeVisulization: React.FC = () => { // useEffect(() => {}, [floatingWidgets]); const handleDrop = async (event: React.DragEvent) => { + event.preventDefault(); + try { event.preventDefault(); const email = localStorage.getItem("email") || ""; @@ -172,7 +175,6 @@ const RealTimeVisulization: React.FC = () => { if (visualizationSocket) { visualizationSocket.emit("v2:viz-float:add", addFloatingWidget); } - // let response = await addingFloatingWidgets( // selectedZone.zoneId, // organization, @@ -198,7 +200,9 @@ const RealTimeVisulization: React.FC = () => { }, })); } catch (error) { } + }; + useEffect(() => { const handleClickOutside = (event: MouseEvent) => { const editWidgetOptions = document.querySelector( @@ -219,31 +223,10 @@ const RealTimeVisulization: React.FC = () => { }; }, [setRightClickSelected]); - // Add this useEffect hook to your component - useEffect(() => { - const handleClickOutside = (event: MouseEvent) => { - const editWidgetOptions = document.querySelector( - ".editWidgetOptions-wrapper" - ); - if ( - editWidgetOptions && - !editWidgetOptions.contains(event.target as Node) - ) { - setRightClickSelected(null); - setRightSelect(null); - } - }; - document.addEventListener("mousedown", handleClickOutside); - return () => { - document.removeEventListener("mousedown", handleClickOutside); - }; - }, [setRightClickSelected]); return ( <> - -
{ }} onDrop={(event) => handleDrop(event)} onDragOver={(event) => event.preventDefault()} + >
diff --git a/app/src/components/ui/componets/zoneAssets.tsx b/app/src/components/ui/componets/zoneAssets.tsx new file mode 100644 index 0000000..daa5a60 --- /dev/null +++ b/app/src/components/ui/componets/zoneAssets.tsx @@ -0,0 +1,42 @@ +import React, { useEffect, useRef } from 'react' +import { useSelectedFloorItem, useZoneAssetId } from '../../../store/store'; +import * as THREE from "three"; +import { useThree } from '@react-three/fiber'; +import * as Types from "../../../types/world/worldTypes"; +export default function ZoneAssets() { + const { zoneAssetId, setZoneAssetId } = useZoneAssetId(); + const { setSelectedFloorItem } = useSelectedFloorItem(); + const { raycaster, controls, scene }: any = useThree(); + useEffect(() => { + // console.log('zoneAssetId: ', zoneAssetId); + if (!zoneAssetId) return + console.log('zoneAssetId: ', zoneAssetId); + let AssetMesh = scene.getObjectByProperty("uuid", zoneAssetId.id); + if (!AssetMesh) return; + + const bbox = new THREE.Box3().setFromObject(AssetMesh); + const size = bbox.getSize(new THREE.Vector3()); + const center = bbox.getCenter(new THREE.Vector3()); + + const front = new THREE.Vector3(0, 0, 1); + AssetMesh.localToWorld(front); + front.sub(AssetMesh.position).normalize(); + + const distance = Math.max(size.x, size.y, size.z) * 2; + const newPosition = center.clone().addScaledVector(front, distance); + + controls.setPosition(newPosition.x, newPosition.y, newPosition.z, true); + controls.setTarget(center.x, center.y, center.z, true); + controls.fitToBox(AssetMesh, true, { cover: true, paddingTop: 5, paddingLeft: 5, paddingBottom: 5, paddingRight: 5, }); + + setSelectedFloorItem(AssetMesh); + + }, [zoneAssetId, scene, controls]) + + + + return ( + <> + + ) +} \ No newline at end of file diff --git a/app/src/components/ui/list/DropDownList.tsx b/app/src/components/ui/list/DropDownList.tsx index 3260eb6..faa5f4e 100644 --- a/app/src/components/ui/list/DropDownList.tsx +++ b/app/src/components/ui/list/DropDownList.tsx @@ -2,7 +2,7 @@ import React, { useEffect, useState } from "react"; import List from "./List"; import { AddIcon, ArrowIcon, FocusIcon } from "../../icons/ExportCommonIcons"; import KebabMenuListMultiSelect from "./KebebMenuListMultiSelect"; -import { useZones } from "../../../store/store"; +import { useFloorItems, useZones } from "../../../store/store"; import { useSelectedZoneStore } from "../../../store/useZoneStore"; interface DropDownListProps { @@ -38,92 +38,66 @@ const DropDownList: React.FC = ({ const handleToggle = () => { setIsOpen((prev) => !prev); // Toggle the state }; - interface Asset { id: string; name: string; + position: [number, number, number]; // x, y, z } - const [zoneDataList, setZoneDataList] = useState< - { id: string; name: string; assets: Asset[] }[] - >([]); - const [zonePoints3D, setZonePoints3D] = useState<[]>([]); + interface Zone { + zoneId: string; + zoneName: string; + points: [number, number, number][]; // polygon vertices + } + interface ZoneData { + id: string; + name: string; + assets: { id: string; name: string; position?: [] ;rotation?:{}}[]; + } + const [zoneDataList, setZoneDataList] = useState([]); + const { floorItems, setFloorItems } = useFloorItems(); - const { selectedZone, setSelectedZone } = useSelectedZoneStore(); + const isPointInsidePolygon = (point: [number, number], polygon: [number, number][]) => { + let inside = false; + for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) { + const xi = polygon[i][0], zi = polygon[i][1]; + const xj = polygon[j][0], zj = polygon[j][1]; - useEffect(() => { - // const value = (zones || []).map( - // (val: { zoneId: string; zoneName: string }) => ({ - // id: val.zoneId, - // name: val.zoneName, - // }) - // ); - // console.log('zones: ', zones); - const value = (zones || []).map((val: { zoneId: string; zoneName: string }) => ({ - id: val.zoneId, - name: val.zoneName - })); - setZoneDataList(prev => (JSON.stringify(prev) !== JSON.stringify(value) ? value : prev)); - const allPoints = zones.flatMap((zone: any) => zone.points); - setZonePoints3D(allPoints); - // setZoneDataList([ - // { - // id: "zone1", - // name: "Zone 1", - // assets: [ - // { - // id: "asset1", - // name: "Asset 1", - // }, - // { - // id: "asset2", - // name: "Asset 2", - // }, - // { - // id: "asset3", - // name: "Asset 3", - // }, - // ], - // }, - // { - // id: "zone2", - // name: "Zone 2", - // assets: [ - // { - // id: "asset4", - // name: "Asset 4", - // }, - // { - // id: "asset5", - // name: "Asset 5", - // }, - // { - // id: "asset6", - // name: "Asset 6", - // }, - // ], - // }, - // { - // id: "zone3", - // name: "Zone 3", - // assets: [ - // { - // id: "asset7", - // name: "Asset 7", - // }, - // { - // id: "asset8", - // name: "Asset 8", - // }, - // ], - // }, - // ]); + const intersect = ((zi > point[1]) !== (zj > point[1])) && + (point[0] < (xj - xi) * (point[1] - zi) / (zj - zi + 0.000001) + xi); - }, [zones]); + if (intersect) inside = !inside; + } + return inside; + }; useEffect(() => { - // console.log('zonePoints3D: ', zonePoints3D); - }, [zonePoints3D]) + const updatedZoneList: ZoneData[] = zones.map((zone: Zone) => { + const polygon2D = zone.points.map((p: [number, number, number]) => [p[0], p[2]]) as [number, number][]; + + const assetsInZone = floorItems + .filter((item: any) => { + const [x, , z] = item.position; + return isPointInsidePolygon([x, z], polygon2D); + }) + .map((item: any) => ({ + id: item.modeluuid, + name: item.modelname, + position: item.position, + rotation:item.rotation + })); + + return { + id: zone.zoneId, + name: zone.zoneName, + assets: assetsInZone, + }; + }); + setZoneDataList(updatedZoneList); + }, [zones, floorItems]); + + + return (
diff --git a/app/src/components/ui/list/List.tsx b/app/src/components/ui/list/List.tsx index 000bc20..a176e14 100644 --- a/app/src/components/ui/list/List.tsx +++ b/app/src/components/ui/list/List.tsx @@ -12,10 +12,14 @@ import { LockIcon, RmoveIcon, } from "../../icons/ExportCommonIcons"; +import { useThree } from "@react-three/fiber"; +import { useZoneAssetId } from "../../../store/store"; interface Asset { id: string; name: string; + position?: [number, number, number]; // Proper 3D vector + rotation?: { x: number; y: number; z: number }; // Proper rotation format } interface ZoneItem { @@ -33,11 +37,13 @@ interface ListProps { const List: React.FC = ({ items = [], remove }) => { const { activeModule, setActiveModule } = useModuleStore(); const { selectedZone, setSelectedZone } = useSelectedZoneStore(); + const { zoneAssetId, setZoneAssetId } = useZoneAssetId(); const { setSubModule } = useSubModuleStore(); const [expandedZones, setExpandedZones] = useState>( {} ); + useEffect(() => { useSelectedZoneStore.getState().setSelectedZone({ zoneName: "", @@ -88,7 +94,9 @@ const List: React.FC = ({ items = [], remove }) => { console.error("Error selecting zone:", error); } } - + function handleAssetClick(asset: Asset) { + setZoneAssetId(asset) + } return ( <> {items.length > 0 ? ( @@ -139,7 +147,7 @@ const List: React.FC = ({ items = [], remove }) => { className="list-container asset-item" >
-
+
handleAssetClick(asset)}>
diff --git a/app/src/components/ui/realTimeVis/floating/SimpleCard.tsx b/app/src/components/ui/realTimeVis/floating/SimpleCard.tsx index 0c36977..7fafb79 100644 --- a/app/src/components/ui/realTimeVis/floating/SimpleCard.tsx +++ b/app/src/components/ui/realTimeVis/floating/SimpleCard.tsx @@ -5,7 +5,7 @@ interface SimpleCardProps { icon: React.ComponentType>; // React component for SVG icon value: string; per: string; // Percentage change - position?: [number, number] + position?: [number, number]; } const SimpleCard: React.FC = ({ @@ -15,7 +15,6 @@ const SimpleCard: React.FC = ({ per, position = [0, 0], }) => { - const handleDragStart = (event: React.DragEvent) => { const rect = event.currentTarget.getBoundingClientRect(); // Get position const cardData = JSON.stringify({ @@ -23,7 +22,7 @@ const SimpleCard: React.FC = ({ value, per, icon: Icon, - + className: event.currentTarget.className, position: [rect.top, rect.left], // ✅ Store position }); diff --git a/app/src/modules/builder/csg/csg.tsx b/app/src/modules/builder/csg/csg.tsx index f84fad0..7e49598 100644 --- a/app/src/modules/builder/csg/csg.tsx +++ b/app/src/modules/builder/csg/csg.tsx @@ -1,6 +1,6 @@ import * as THREE from "three"; import { Geometry, Base, Subtraction } from "@react-three/csg"; -import { useDeleteModels } from "../../../store/store"; +import { useDeleteTool } from "../../../store/store"; import { useRef } from "react"; export interface CsgProps { @@ -11,19 +11,19 @@ export interface CsgProps { } export const Csg: React.FC = (props) => { - const { deleteModels } = useDeleteModels(); + const { deleteTool } = useDeleteTool(); const modelRef = useRef(); const originalMaterials = useRef>(new Map()); const handleHover = (hovered: boolean, object: THREE.Mesh | null) => { - if (modelRef.current && deleteModels) { + if (modelRef.current && deleteTool) { modelRef.current.traverse((child) => { if (child instanceof THREE.Mesh) { if (!originalMaterials.current.has(child)) { originalMaterials.current.set(child, child.material); } child.material = child.material.clone(); - child.material.color.set(hovered && deleteModels ? 0xff0000 : (originalMaterials.current.get(child) as any).color); + child.material.color.set(hovered && deleteTool ? 0xff0000 : (originalMaterials.current.get(child) as any).color); } }); } diff --git a/app/src/modules/builder/geomentries/assets/addAssetModel.ts b/app/src/modules/builder/geomentries/assets/addAssetModel.ts index 9fc6199..d66d019 100644 --- a/app/src/modules/builder/geomentries/assets/addAssetModel.ts +++ b/app/src/modules/builder/geomentries/assets/addAssetModel.ts @@ -219,7 +219,7 @@ async function handleModelLoad( eventData.position = newFloorItem.position; eventData.rotation = [model.rotation.x, model.rotation.y, model.rotation.z]; - setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema)[]) => [ + setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | Types.ArmBotEventsSchema)[]) => [ ...(prevEvents || []), eventData as Types.ConveyorEventsSchema ]); @@ -281,13 +281,139 @@ async function handleModelLoad( return updatedItems; }); - setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema)[]) => [ + setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | Types.ArmBotEventsSchema)[]) => [ ...(prevEvents || []), eventData as Types.VehicleEventsSchema ]); socket.emit("v2:model-asset:add", data); + } else if (res.type === "StaticMachine") { + + const pointUUID = THREE.MathUtils.generateUUID(); + + const backendEventData: Extract = { + type: "StaticMachine", + points: { + uuid: pointUUID, + position: res.points.position as [number, number, number], + actions: { uuid: THREE.MathUtils.generateUUID(), name: 'Action 1', buffer: 0, material: 'Inherit' }, + triggers: { uuid: THREE.MathUtils.generateUUID(), name: 'Trigger 1', type: 'OnComplete' }, + connections: { source: { modelUUID: model.uuid, pointUUID: pointUUID }, targets: [] }, + } + } + + // API + + // await setFloorItemApi( + // organization, + // newFloorItem.modeluuid, + // newFloorItem.modelname, + // newFloorItem.modelfileID, + // newFloorItem.position, + // { x: model.rotation.x, y: model.rotation.y, z: model.rotation.z }, + // false, + // true, + // { type: backendEventData.type, points: backendEventData.points } + // ); + + // SOCKET + + const data = { + organization, + modeluuid: newFloorItem.modeluuid, + modelname: newFloorItem.modelname, + modelfileID: newFloorItem.modelfileID, + position: newFloorItem.position, + rotation: { x: model.rotation.x, y: model.rotation.y, z: model.rotation.z }, + isLocked: false, + isVisible: true, + eventData: { type: backendEventData.type, points: backendEventData.points }, + socketId: socket.id + }; + + const eventData: any = backendEventData; + eventData.modeluuid = newFloorItem.modeluuid; + eventData.modelName = newFloorItem.modelname; + eventData.position = newFloorItem.position; + eventData.rotation = [model.rotation.x, model.rotation.y, model.rotation.z]; + + setFloorItems((prevItems) => { + const updatedItems = [...(prevItems || []), newFloorItem]; + localStorage.setItem("FloorItems", JSON.stringify(updatedItems)); + return updatedItems; + }); + + setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | Types.ArmBotEventsSchema)[]) => [ + ...(prevEvents || []), + eventData as Types.StaticMachineEventsSchema + ]); + + socket.emit("v2:model-asset:add", data); + + } else if (res.type === "ArmBot") { + + const pointUUID = THREE.MathUtils.generateUUID(); + + const backendEventData: Extract = { + type: "ArmBot", + points: { + uuid: pointUUID, + position: res.points.position as [number, number, number], + actions: { uuid: THREE.MathUtils.generateUUID(), name: 'Action 1', speed: 1, processes: [] }, + triggers: { uuid: THREE.MathUtils.generateUUID(), name: 'Trigger 1', type: 'OnComplete' }, + connections: { source: { modelUUID: model.uuid, pointUUID: pointUUID }, targets: [] }, + } + } + + // API + + // await setFloorItemApi( + // organization, + // newFloorItem.modeluuid, + // newFloorItem.modelname, + // newFloorItem.modelfileID, + // newFloorItem.position, + // { x: model.rotation.x, y: model.rotation.y, z: model.rotation.z }, + // false, + // true, + // { type: backendEventData.type, points: backendEventData.points } + // ); + + // SOCKET + + const data = { + organization, + modeluuid: newFloorItem.modeluuid, + modelname: newFloorItem.modelname, + modelfileID: newFloorItem.modelfileID, + position: newFloorItem.position, + rotation: { x: model.rotation.x, y: model.rotation.y, z: model.rotation.z }, + isLocked: false, + isVisible: true, + eventData: { type: backendEventData.type, points: backendEventData.points }, + socketId: socket.id + }; + + const eventData: any = backendEventData; + eventData.modeluuid = newFloorItem.modeluuid; + eventData.modelName = newFloorItem.modelname; + eventData.position = newFloorItem.position; + eventData.rotation = [model.rotation.x, model.rotation.y, model.rotation.z]; + + setFloorItems((prevItems) => { + const updatedItems = [...(prevItems || []), newFloorItem]; + localStorage.setItem("FloorItems", JSON.stringify(updatedItems)); + return updatedItems; + }); + + setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | Types.ArmBotEventsSchema)[]) => [ + ...(prevEvents || []), + eventData as Types.ArmBotEventsSchema + ]); + + socket.emit("v2:model-asset:add", data); + } else { // API diff --git a/app/src/modules/builder/groups/floorGroup.tsx b/app/src/modules/builder/groups/floorGroup.tsx index da3536b..e589380 100644 --- a/app/src/modules/builder/groups/floorGroup.tsx +++ b/app/src/modules/builder/groups/floorGroup.tsx @@ -1,5 +1,5 @@ import { useFrame, useThree } from "@react-three/fiber"; -import { useAddAction, useDeleteModels, useRoofVisibility, useToggleView, useWallVisibility, useUpdateScene } from "../../../store/store"; +import { useAddAction, useDeleteTool, useRoofVisibility, useToggleView, useWallVisibility, useUpdateScene } from "../../../store/store"; import hideRoof from "../geomentries/roofs/hideRoof"; import hideWalls from "../geomentries/walls/hideWalls"; import addAndUpdateReferencePillar from "../geomentries/pillars/addAndUpdateReferencePillar"; @@ -16,7 +16,7 @@ const FloorGroup = ({ floorGroup, lines, referencePole, hoveredDeletablePillar } const { toggleView, setToggleView } = useToggleView(); const { scene, camera, pointer, raycaster, gl } = useThree(); const { addAction, setAddAction } = useAddAction(); - const { deleteModels, setDeleteModels } = useDeleteModels(); + const { deleteTool, setDeleteTool } = useDeleteTool(); const { updateScene, setUpdateScene } = useUpdateScene(); useEffect(() => { @@ -56,7 +56,7 @@ const FloorGroup = ({ floorGroup, lines, referencePole, hoveredDeletablePillar } if (addAction === "pillar") { addPillar(referencePole, floorGroup); } - if (deleteModels) { + if (deleteTool) { DeletePillar(hoveredDeletablePillar, floorGroup); } } @@ -78,7 +78,7 @@ const FloorGroup = ({ floorGroup, lines, referencePole, hoveredDeletablePillar } canvasElement.removeEventListener("mouseup", onMouseUp); canvasElement.removeEventListener("mousemove", onMouseMove); }; - }, [deleteModels, addAction]) + }, [deleteTool, addAction]) useFrame(() => { hideRoof(roofVisibility, floorGroup, camera); @@ -87,7 +87,7 @@ const FloorGroup = ({ floorGroup, lines, referencePole, hoveredDeletablePillar } if (addAction === "pillar") { addAndUpdateReferencePillar(raycaster, floorGroup, referencePole); } - if (deleteModels) { + if (deleteTool) { DeletableHoveredPillar(state, floorGroup, hoveredDeletablePillar); } }) diff --git a/app/src/modules/builder/groups/floorItemsGroup.tsx b/app/src/modules/builder/groups/floorItemsGroup.tsx index 6289bd0..bc0791e 100644 --- a/app/src/modules/builder/groups/floorItemsGroup.tsx +++ b/app/src/modules/builder/groups/floorItemsGroup.tsx @@ -1,5 +1,5 @@ import { useFrame, useThree } from "@react-three/fiber"; -import { useActiveTool, useAsset3dWidget, useCamMode, useDeletableFloorItem, useDeleteModels, useFloorItems, useLoadingProgress, useRenderDistance, useselectedFloorItem, useSelectedItem, useSimulationStates, useSocketStore, useToggleView, useTransformMode, } from "../../../store/store"; +import { useActiveTool, useAsset3dWidget, useCamMode, useDeletableFloorItem, useDeleteTool, useFloorItems, useLoadingProgress, useRenderDistance, useSelectedFloorItem, useSelectedItem, useSimulationStates, useSocketStore, useToggleView, useTransformMode, } from "../../../store/store"; import assetVisibility from "../geomentries/assets/assetVisibility"; import { useEffect } from "react"; import * as THREE from "three"; @@ -26,10 +26,10 @@ const FloorItemsGroup = ({ itemsGroup, hoveredDeletableFloorItem, AttachedObject const { toggleView } = useToggleView(); const { floorItems, setFloorItems } = useFloorItems(); const { camMode } = useCamMode(); - const { deleteModels } = useDeleteModels(); + const { deleteTool } = useDeleteTool(); const { setDeletableFloorItem } = useDeletableFloorItem(); const { transformMode } = useTransformMode(); - const { setselectedFloorItem } = useselectedFloorItem(); + const { setSelectedFloorItem } = useSelectedFloorItem(); const { activeTool } = useActiveTool(); const { selectedItem, setSelectedItem } = useSelectedItem(); const { simulationStates, setSimulationStates } = useSimulationStates(); @@ -191,8 +191,10 @@ const FloorItemsGroup = ({ itemsGroup, hoveredDeletableFloorItem, AttachedObject isLeftMouseDown = false; if (drag) return; - if (deleteModels) { + if (deleteTool) { DeleteFloorItems(itemsGroup, hoveredDeletableFloorItem, setFloorItems, setSimulationStates, socket); + + // Remove EventData if there are any in the asset. } const Mode = transformMode; @@ -212,12 +214,12 @@ const FloorItemsGroup = ({ itemsGroup, hoveredDeletableFloorItem, AttachedObject // } // if (currentObject) { // AttachedObject.current = currentObject as any; - // setselectedFloorItem(AttachedObject.current!); + // setSelectedFloorItem(AttachedObject.current!); // } } else { const target = controls.getTarget(new THREE.Vector3()); await controls.setTarget(target.x, 0, target.z, true); - setselectedFloorItem(null); + setSelectedFloorItem(null); } } } @@ -261,12 +263,12 @@ const FloorItemsGroup = ({ itemsGroup, hoveredDeletableFloorItem, AttachedObject controls.setTarget(center.x, center.y, center.z, true); controls.fitToBox(AttachedObject.current!, true, { cover: true, paddingTop: 5, paddingLeft: 5, paddingBottom: 5, paddingRight: 5, }); - setselectedFloorItem(AttachedObject.current!); + setSelectedFloorItem(AttachedObject.current!); } } else { const target = controls.getTarget(new THREE.Vector3()); await controls.setTarget(target.x, 0, target.z, true); - setselectedFloorItem(null); + setSelectedFloorItem(null); } } } @@ -295,7 +297,7 @@ const FloorItemsGroup = ({ itemsGroup, hoveredDeletableFloorItem, AttachedObject if (controls) { const target = controls.getTarget(new THREE.Vector3()); controls.setTarget(target.x, 0, target.z, true); - setselectedFloorItem(null); + setSelectedFloorItem(null); } } @@ -307,14 +309,14 @@ const FloorItemsGroup = ({ itemsGroup, hoveredDeletableFloorItem, AttachedObject canvasElement.removeEventListener("drop", onDrop); canvasElement.removeEventListener("dragover", onDragOver); }; - }, [deleteModels, transformMode, controls, selectedItem, state.camera, state.pointer, activeTool, activeModule,]); + }, [deleteTool, transformMode, controls, selectedItem, state.camera, state.pointer, activeTool, activeModule,]); useFrame(() => { if (controls) assetVisibility(itemsGroup, state.camera.position, renderDistance); - if (deleteModels && activeModule === "builder") { + if (deleteTool && activeModule === "builder") { DeletableHoveredFloorItems(state, itemsGroup, hoveredDeletableFloorItem, setDeletableFloorItem); - } else if (!deleteModels) { + } else if (!deleteTool) { if (hoveredDeletableFloorItem.current) { hoveredDeletableFloorItem.current = undefined; setDeletableFloorItem(null); diff --git a/app/src/modules/builder/groups/wallItemsGroup.tsx b/app/src/modules/builder/groups/wallItemsGroup.tsx index 1439ef5..cdc326e 100644 --- a/app/src/modules/builder/groups/wallItemsGroup.tsx +++ b/app/src/modules/builder/groups/wallItemsGroup.tsx @@ -1,5 +1,5 @@ import { useEffect } from "react"; -import { useDeleteModels, useDeletePointOrLine, useObjectPosition, useObjectRotation, useObjectScale, useSelectedWallItem, useSocketStore, useWallItems } from "../../../store/store"; +import { useDeleteTool, useDeletePointOrLine, useObjectPosition, useObjectRotation, useObjectScale, useSelectedWallItem, useSocketStore, useWallItems } from "../../../store/store"; import { Csg } from "../csg/csg"; import * as Types from "../../../types/world/worldTypes"; import * as CONSTANTS from "../../../types/world/worldConstants"; @@ -16,7 +16,7 @@ const WallItemsGroup = ({ currentWallItem, AssetConfigurations, hoveredDeletable const state = useThree(); const { socket } = useSocketStore(); const { pointer, camera, raycaster } = state; - const { deleteModels, setDeleteModels } = useDeleteModels(); + const { deleteTool, setDeleteTool } = useDeleteTool(); const { wallItems, setWallItems } = useWallItems(); const { objectPosition, setObjectPosition } = useObjectPosition(); const { objectScale, setObjectScale } = useObjectScale(); @@ -210,7 +210,7 @@ const WallItemsGroup = ({ currentWallItem, AssetConfigurations, hoveredDeletable const onMouseUp = (evt: any) => { if (evt.button === 0) { isLeftMouseDown = false; - if (!drag && deleteModels && activeModule === "builder") { + if (!drag && deleteTool && activeModule === "builder") { DeleteWallItems(hoveredDeletableWallItem, setWallItems, wallItems, socket); } } @@ -257,15 +257,15 @@ const WallItemsGroup = ({ currentWallItem, AssetConfigurations, hoveredDeletable canvasElement.removeEventListener("drop", onDrop); canvasElement.removeEventListener("dragover", onDragOver); }; - }, [deleteModels, wallItems]) + }, [deleteTool, wallItems]) useEffect(() => { - if (deleteModels && activeModule === "builder") { + if (deleteTool && activeModule === "builder") { handleMeshMissed(currentWallItem, setSelectedWallItem, setSelectedItemsIndex); setSelectedWallItem(null); setSelectedItemsIndex(null); } - }, [deleteModels]) + }, [deleteTool]) return ( <> diff --git a/app/src/modules/builder/groups/wallsAndWallItems.tsx b/app/src/modules/builder/groups/wallsAndWallItems.tsx index 7366f28..6b6c97d 100644 --- a/app/src/modules/builder/groups/wallsAndWallItems.tsx +++ b/app/src/modules/builder/groups/wallsAndWallItems.tsx @@ -1,5 +1,5 @@ import { Geometry } from "@react-three/csg"; -import { useDeleteModels, useSelectedWallItem, useToggleView, useTransformMode, useWallItems, useWalls } from "../../../store/store"; +import { useDeleteTool, useSelectedWallItem, useToggleView, useTransformMode, useWallItems, useWalls } from "../../../store/store"; import handleMeshDown from "../eventFunctions/handleMeshDown"; import handleMeshMissed from "../eventFunctions/handleMeshMissed"; import WallsMesh from "./wallsMesh"; @@ -11,13 +11,13 @@ const WallsAndWallItems = ({ CSGGroup, AssetConfigurations, setSelectedItemsInde const { walls, setWalls } = useWalls(); const { wallItems, setWallItems } = useWallItems(); const { toggleView, setToggleView } = useToggleView(); - const { deleteModels, setDeleteModels } = useDeleteModels(); + const { deleteTool, setDeleteTool } = useDeleteTool(); const { transformMode, setTransformMode } = useTransformMode(); const { selectedWallItem, setSelectedWallItem } = useSelectedWallItem(); useEffect(() => { if (transformMode === null) { - if (!deleteModels) { + if (!deleteTool) { handleMeshMissed(currentWallItem, setSelectedWallItem, setSelectedItemsIndex); setSelectedWallItem(null); setSelectedItemsIndex(null); @@ -33,12 +33,12 @@ const WallsAndWallItems = ({ CSGGroup, AssetConfigurations, setSelectedItemsInde receiveShadow visible={!toggleView} onClick={(event) => { - if (!deleteModels && transformMode !== null) { + if (!deleteTool && transformMode !== null) { handleMeshDown(event, currentWallItem, setSelectedWallItem, setSelectedItemsIndex, wallItems, toggleView); } }} onPointerMissed={() => { - if (!deleteModels) { + if (!deleteTool) { handleMeshMissed(currentWallItem, setSelectedWallItem, setSelectedItemsIndex); setSelectedWallItem(null); setSelectedItemsIndex(null); diff --git a/app/src/modules/builder/groups/zoneGroup.tsx b/app/src/modules/builder/groups/zoneGroup.tsx index c5e54d3..d245516 100644 --- a/app/src/modules/builder/groups/zoneGroup.tsx +++ b/app/src/modules/builder/groups/zoneGroup.tsx @@ -2,7 +2,7 @@ import React, { useState, useEffect, useMemo, useRef } from "react"; import { Line, Sphere } from "@react-three/drei"; import { useThree, useFrame } from "@react-three/fiber"; import * as THREE from "three"; -import { useActiveLayer, useDeleteModels, useDeletePointOrLine, useMovePoint, useSocketStore, useToggleView, useToolMode, useRemovedLayer, useZones, useZonePoints } from "../../../store/store"; +import { useActiveLayer, useDeleteTool, useDeletePointOrLine, useMovePoint, useSocketStore, useToggleView, useToolMode, useRemovedLayer, useZones, useZonePoints } from "../../../store/store"; // import { setZonesApi } from "../../../services/factoryBuilder/zones/setZonesApi"; // import { deleteZonesApi } from "../../../services/factoryBuilder/zones/deleteZoneApi"; import { getZonesApi } from "../../../services/factoryBuilder/zones/getZonesApi"; @@ -23,7 +23,7 @@ const ZoneGroup: React.FC = () => { const { removedLayer, setRemovedLayer } = useRemovedLayer(); const { toolMode, setToolMode } = useToolMode(); const { movePoint, setMovePoint } = useMovePoint(); - const { deleteModels, setDeleteModels } = useDeleteModels(); + const { deleteTool, setDeleteTool } = useDeleteTool(); const { activeLayer, setActiveLayer } = useActiveLayer(); const { socket } = useSocketStore(); @@ -115,7 +115,7 @@ const ZoneGroup: React.FC = () => { } else { setDeletePointOrLine(false); setMovePoint(false); - setDeleteModels(false); + setDeleteTool(false); } if (!toggleView) { setStartPoint(null); diff --git a/app/src/modules/builder/groups/zoneGroup1.tsx b/app/src/modules/builder/groups/zoneGroup1.tsx index b0c6638..80be9a3 100644 --- a/app/src/modules/builder/groups/zoneGroup1.tsx +++ b/app/src/modules/builder/groups/zoneGroup1.tsx @@ -2,7 +2,7 @@ import { useEffect } from "react"; import * as THREE from 'three'; import * as Types from '../../../types/world/worldTypes'; import * as CONSTANTS from "../../../types/world/worldConstants"; -import { useActiveLayer, useSocketStore, useDeleteModels, useDeletePointOrLine, useMovePoint, useToggleView, useUpdateScene, useNewLines, useToolMode } from "../../../store/store"; +import { useActiveLayer, useSocketStore, useDeleteTool, useDeletePointOrLine, useMovePoint, useToggleView, useUpdateScene, useNewLines, useToolMode } from "../../../store/store"; import { useThree } from "@react-three/fiber"; import arrayLineToObject from "../geomentries/lines/lineConvertions/arrayLineToObject"; import addPointToScene from "../geomentries/points/addPointToScene"; @@ -14,7 +14,7 @@ import loadZones from "../geomentries/zones/loadZones"; const ZoneGroup = ({ zoneGroup, plane, floorPlanGroupLine, floorPlanGroupPoint, line, lines, currentLayerPoint, dragPointControls, floorPlanGroup, ReferenceLineMesh, LineCreated, isSnapped, ispreSnapped, snappedPoint, isSnappedUUID, isAngleSnapped, anglesnappedPoint }: any) => { const { toggleView, setToggleView } = useToggleView(); - const { deleteModels, setDeleteModels } = useDeleteModels(); + const { deleteTool, setDeleteTool } = useDeleteTool(); const { deletePointOrLine, setDeletePointOrLine } = useDeletePointOrLine(); const { toolMode, setToolMode } = useToolMode(); const { movePoint, setMovePoint } = useMovePoint(); @@ -35,7 +35,7 @@ const ZoneGroup = ({ zoneGroup, plane, floorPlanGroupLine, floorPlanGroupPoint, if (toolMode === "Zone") { setDeletePointOrLine(false); setMovePoint(false); - setDeleteModels(false); + setDeleteTool(false); } else { removeSoloPoint(line, floorPlanGroupLine, floorPlanGroupPoint); removeReferenceLine(floorPlanGroup, ReferenceLineMesh, LineCreated, line); diff --git a/app/src/modules/scene/IntialLoad/loadInitialFloorItems.ts b/app/src/modules/scene/IntialLoad/loadInitialFloorItems.ts index 22b9a83..c90fe3b 100644 --- a/app/src/modules/scene/IntialLoad/loadInitialFloorItems.ts +++ b/app/src/modules/scene/IntialLoad/loadInitialFloorItems.ts @@ -192,7 +192,7 @@ function processLoadedModel( }, ]); - if (item.eventData || item.modelfileID === '67e3db5ac2e8f37134526f40' || item.modelfileID === '67eb7904c2e8f37134527eae') { + if (item.eventData || item.modelfileID === '67e3db5ac2e8f37134526f40') { processEventData(item, setSimulationStates); } @@ -210,7 +210,7 @@ function processEventData(item: Types.EventData, setSimulationStates: any) { data.position = item.position; data.rotation = [item.rotation.x, item.rotation.y, item.rotation.z]; - setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema)[]) => [ + setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | Types.ArmBotEventsSchema)[]) => [ ...(prevEvents || []), data as Types.ConveyorEventsSchema ]); @@ -222,58 +222,35 @@ function processEventData(item: Types.EventData, setSimulationStates: any) { data.modelName = item.modelname; data.position = item.position; - setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema)[]) => [ + setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | Types.ArmBotEventsSchema)[]) => [ ...(prevEvents || []), data as Types.VehicleEventsSchema ]); - } else if (item.modelfileID === '67e3db5ac2e8f37134526f40') { + } else if (item.eventData?.type === 'StaticMachine') { - const pointUUID = THREE.MathUtils.generateUUID(); - const pointPosition = new THREE.Vector3(0, 1.5, -0.5); + const data: any = item.eventData; + data.modeluuid = item.modeluuid; + data.modelName = item.modelname; + data.position = item.position; + data.rotation = [item.rotation.x, item.rotation.y, item.rotation.z]; - const staticMachine: Types.StaticMachineEventsSchema = { - modeluuid: item.modeluuid, - modelName: item.modelname, - type: "StaticMachine", - points: { - uuid: pointUUID, - position: [pointPosition.x, pointPosition.y, pointPosition.z], - actions: { uuid: THREE.MathUtils.generateUUID(), name: 'Action 1', buffer: 'Inherit', material: 'Inherit', isUsed: false }, - triggers: { uuid: THREE.MathUtils.generateUUID(), name: 'Trigger 1', type: 'OnComplete' }, - connections: { source: { modelUUID: item.modeluuid, pointUUID: pointUUID }, targets: [] }, - }, - position: item.position, - rotation: [item.rotation.x, item.rotation.y, item.rotation.z], - }; - - setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema)[]) => [ + setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | Types.ArmBotEventsSchema)[]) => [ ...(prevEvents || []), - staticMachine as Types.StaticMachineEventsSchema + data as Types.StaticMachineEventsSchema ]); - } else if (item.modelfileID === '67eb7904c2e8f37134527eae') { - const pointUUID = THREE.MathUtils.generateUUID(); - const pointPosition = new THREE.Vector3(0, 2.75, -0.5); + } else if (item.eventData?.type === 'ArmBot') { - const armBot: Types.ArmBotEventsSchema = { - modeluuid: item.modeluuid, - modelName: item.modelname, - type: "ArmBot", - points: { - uuid: pointUUID, - position: [pointPosition.x, pointPosition.y, pointPosition.z], - actions: { uuid: THREE.MathUtils.generateUUID(), name: 'Action 1', speed: 1, processes: [] }, - triggers: { uuid: THREE.MathUtils.generateUUID(), name: 'Trigger 1', type: 'OnComplete' }, - connections: { source: { modelUUID: item.modeluuid, pointUUID: pointUUID }, targets: [] }, - }, - position: item.position, - rotation: [item.rotation.x, item.rotation.y, item.rotation.z], - } + const data: any = item.eventData; + data.modeluuid = item.modeluuid; + data.modelName = item.modelname; + data.position = item.position; + data.rotation = [item.rotation.x, item.rotation.y, item.rotation.z]; - setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema)[]) => [ + setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | Types.ArmBotEventsSchema)[]) => [ ...(prevEvents || []), - armBot as Types.ArmBotEventsSchema + data as Types.ArmBotEventsSchema ]); } diff --git a/app/src/modules/scene/controls/selection/copyPasteControls.tsx b/app/src/modules/scene/controls/selection/copyPasteControls.tsx index 3d2acee..d315c10 100644 --- a/app/src/modules/scene/controls/selection/copyPasteControls.tsx +++ b/app/src/modules/scene/controls/selection/copyPasteControls.tsx @@ -181,7 +181,10 @@ const CopyPasteControls = ({ itemsGroupRef, copiedObjects, setCopiedObjects, pas uuid: THREE.MathUtils.generateUUID() })) : [defaultAction], - triggers: (eventData as Types.ConveyorEventsSchema)?.points[index].triggers, + triggers: (eventData as Types.ConveyorEventsSchema)?.points[index].triggers.map(trigger => ({ + ...trigger, + uuid: THREE.MathUtils.generateUUID() + })), connections: { source: { modelUUID: obj.uuid, pointUUID }, targets: [] @@ -320,6 +323,163 @@ const CopyPasteControls = ({ itemsGroupRef, copiedObjects, setCopiedObjects, pas socket.emit("v2:model-asset:add", data); + } else if (eventData.type === 'StaticMachine' && eventData) { + const createStaticMachinePoint = () => { + const pointUUID = THREE.MathUtils.generateUUID(); + const vehiclePoint = (eventData as Types.StaticMachineEventsSchema)?.points; + const hasActions = vehiclePoint?.actions !== undefined; + + const defaultAction = { + uuid: THREE.MathUtils.generateUUID(), + name: 'Action 1', + buffer: 0, + material: 'Inherit', + }; + + return { + uuid: pointUUID, + position: vehiclePoint?.position, + actions: hasActions + ? { + ...vehiclePoint.actions, + uuid: THREE.MathUtils.generateUUID() + } + : defaultAction, + connections: { + source: { modelUUID: obj.uuid, pointUUID }, + targets: [] + } + }; + }; + + const backendEventData = { + type: 'StaticMachine', + points: createStaticMachinePoint() + }; + + // API + + // setFloorItemApi( + // organization, + // obj.uuid, + // obj.userData.name, + // obj.userData.modelId, + // [worldPosition.x, worldPosition.y, worldPosition.z], + // { "x": obj.rotation.x, "y": obj.rotation.y, "z": obj.rotation.z }, + // false, + // true, + // { type: backendEventData.type, points: backendEventData.points } + // ); + + // SOCKET + + const data = { + organization, + modeluuid: newFloorItem.modeluuid, + modelname: newFloorItem.modelname, + modelfileID: newFloorItem.modelfileID, + position: newFloorItem.position, + rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z }, + isLocked: false, + isVisible: true, + eventData: { type: backendEventData.type, points: backendEventData.points }, + socketId: socket.id, + }; + + const newEventData: any = { type: backendEventData.type, points: backendEventData.points }; + newEventData.modeluuid = newFloorItem.modeluuid; + newEventData.modelName = newFloorItem.modelname; + newEventData.position = newFloorItem.position; + newEventData.rotation = [obj.rotation.x, obj.rotation.y, obj.rotation.z]; + + setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | Types.ArmBotEventsSchema)[]) => [ + ...(prevEvents || []), + newEventData as Types.StaticMachineEventsSchema + ]); + + socket.emit("v2:model-asset:add", data); + + } else if (eventData.type === 'ArmBot' && eventData) { + const createArmBotPoint = () => { + const pointUUID = THREE.MathUtils.generateUUID(); + const vehiclePoint = (eventData as Types.ArmBotEventsSchema)?.points; + const hasActions = vehiclePoint?.actions !== undefined; + + const defaultAction = { + uuid: THREE.MathUtils.generateUUID(), + name: 'Action 1', + buffer: 0, + material: 'Inherit', + }; + + return { + uuid: pointUUID, + position: vehiclePoint?.position, + actions: hasActions + ? { + ...vehiclePoint.actions, + uuid: THREE.MathUtils.generateUUID() + } + : defaultAction, + triggers: { + uuid: THREE.MathUtils.generateUUID(), + name: vehiclePoint.triggers.name, + type: vehiclePoint.triggers.type, + }, + connections: { + source: { modelUUID: obj.uuid, pointUUID }, + targets: [] + } + }; + }; + + const backendEventData = { + type: 'ArmBot', + points: createArmBotPoint() + }; + + // API + + // setFloorItemApi( + // organization, + // obj.uuid, + // obj.userData.name, + // obj.userData.modelId, + // [worldPosition.x, worldPosition.y, worldPosition.z], + // { "x": obj.rotation.x, "y": obj.rotation.y, "z": obj.rotation.z }, + // false, + // true, + // { type: backendEventData.type, points: backendEventData.points } + // ); + + // SOCKET + + const data = { + organization, + modeluuid: newFloorItem.modeluuid, + modelname: newFloorItem.modelname, + modelfileID: newFloorItem.modelfileID, + position: newFloorItem.position, + rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z }, + isLocked: false, + isVisible: true, + eventData: { type: backendEventData.type, points: backendEventData.points }, + socketId: socket.id, + }; + + const newEventData: any = { type: backendEventData.type, points: backendEventData.points }; + newEventData.modeluuid = newFloorItem.modeluuid; + newEventData.modelName = newFloorItem.modelname; + newEventData.position = newFloorItem.position; + newEventData.rotation = [obj.rotation.x, obj.rotation.y, obj.rotation.z]; + + setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | Types.ArmBotEventsSchema)[]) => [ + ...(prevEvents || []), + newEventData as Types.ArmBotEventsSchema + ]); + + socket.emit("v2:model-asset:add", data); + } else { //REST diff --git a/app/src/modules/scene/controls/selection/duplicationControls.tsx b/app/src/modules/scene/controls/selection/duplicationControls.tsx index a71412d..9c987d0 100644 --- a/app/src/modules/scene/controls/selection/duplicationControls.tsx +++ b/app/src/modules/scene/controls/selection/duplicationControls.tsx @@ -163,7 +163,10 @@ const DuplicationControls = ({ itemsGroupRef, duplicatedObjects, setDuplicatedOb uuid: THREE.MathUtils.generateUUID() })) : [defaultAction], - triggers: (eventData as Types.ConveyorEventsSchema)?.points[index].triggers, + triggers: (eventData as Types.ConveyorEventsSchema)?.points[index].triggers.map(trigger => ({ + ...trigger, + uuid: THREE.MathUtils.generateUUID() + })), connections: { source: { modelUUID: newFloorItem.modeluuid, pointUUID }, targets: [] @@ -174,9 +177,9 @@ const DuplicationControls = ({ itemsGroupRef, duplicatedObjects, setDuplicatedOb const backendEventData = { type: 'Conveyor', points: [ - createConveyorPoint(0), // point1 - createConveyorPoint(1), // middlePoint - createConveyorPoint(2) // point2 + createConveyorPoint(0), + createConveyorPoint(1), + createConveyorPoint(2) ], speed: (eventData as Types.ConveyorEventsSchema)?.speed }; @@ -302,6 +305,163 @@ const DuplicationControls = ({ itemsGroupRef, duplicatedObjects, setDuplicatedOb socket.emit("v2:model-asset:add", data); + } else if (eventData.type === 'StaticMachine' && eventData) { + const createStaticMachinePoint = () => { + const pointUUID = THREE.MathUtils.generateUUID(); + const vehiclePoint = (eventData as Types.StaticMachineEventsSchema)?.points; + const hasActions = vehiclePoint?.actions !== undefined; + + const defaultAction = { + uuid: THREE.MathUtils.generateUUID(), + name: 'Action 1', + buffer: 0, + material: 'Inherit', + }; + + return { + uuid: pointUUID, + position: vehiclePoint?.position, + actions: hasActions + ? { + ...vehiclePoint.actions, + uuid: THREE.MathUtils.generateUUID() + } + : defaultAction, + connections: { + source: { modelUUID: obj.uuid, pointUUID }, + targets: [] + } + }; + }; + + const backendEventData = { + type: 'StaticMachine', + points: createStaticMachinePoint() + }; + + // API + + // setFloorItemApi( + // organization, + // obj.uuid, + // obj.userData.name, + // obj.userData.modelId, + // [worldPosition.x, worldPosition.y, worldPosition.z], + // { "x": obj.rotation.x, "y": obj.rotation.y, "z": obj.rotation.z }, + // false, + // true, + // { type: backendEventData.type, points: backendEventData.points } + // ); + + // SOCKET + + const data = { + organization, + modeluuid: newFloorItem.modeluuid, + modelname: newFloorItem.modelname, + modelfileID: newFloorItem.modelfileID, + position: newFloorItem.position, + rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z }, + isLocked: false, + isVisible: true, + eventData: { type: backendEventData.type, points: backendEventData.points }, + socketId: socket.id, + }; + + const newEventData: any = { type: backendEventData.type, points: backendEventData.points }; + newEventData.modeluuid = newFloorItem.modeluuid; + newEventData.modelName = newFloorItem.modelname; + newEventData.position = newFloorItem.position; + newEventData.rotation = [obj.rotation.x, obj.rotation.y, obj.rotation.z]; + + setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | Types.ArmBotEventsSchema)[]) => [ + ...(prevEvents || []), + newEventData as Types.StaticMachineEventsSchema + ]); + + socket.emit("v2:model-asset:add", data); + + } else if (eventData.type === 'ArmBot' && eventData) { + const createArmBotPoint = () => { + const pointUUID = THREE.MathUtils.generateUUID(); + const vehiclePoint = (eventData as Types.ArmBotEventsSchema)?.points; + const hasActions = vehiclePoint?.actions !== undefined; + + const defaultAction = { + uuid: THREE.MathUtils.generateUUID(), + name: 'Action 1', + buffer: 0, + material: 'Inherit', + }; + + return { + uuid: pointUUID, + position: vehiclePoint?.position, + actions: hasActions + ? { + ...vehiclePoint.actions, + uuid: THREE.MathUtils.generateUUID() + } + : defaultAction, + triggers: { + uuid: THREE.MathUtils.generateUUID(), + name: vehiclePoint.triggers.name, + type: vehiclePoint.triggers.type, + }, + connections: { + source: { modelUUID: obj.uuid, pointUUID }, + targets: [] + } + }; + }; + + const backendEventData = { + type: 'ArmBot', + points: createArmBotPoint() + }; + + // API + + // setFloorItemApi( + // organization, + // obj.uuid, + // obj.userData.name, + // obj.userData.modelId, + // [worldPosition.x, worldPosition.y, worldPosition.z], + // { "x": obj.rotation.x, "y": obj.rotation.y, "z": obj.rotation.z }, + // false, + // true, + // { type: backendEventData.type, points: backendEventData.points } + // ); + + // SOCKET + + const data = { + organization, + modeluuid: newFloorItem.modeluuid, + modelname: newFloorItem.modelname, + modelfileID: newFloorItem.modelfileID, + position: newFloorItem.position, + rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z }, + isLocked: false, + isVisible: true, + eventData: { type: backendEventData.type, points: backendEventData.points }, + socketId: socket.id, + }; + + const newEventData: any = { type: backendEventData.type, points: backendEventData.points }; + newEventData.modeluuid = newFloorItem.modeluuid; + newEventData.modelName = newFloorItem.modelname; + newEventData.position = newFloorItem.position; + newEventData.rotation = [obj.rotation.x, obj.rotation.y, obj.rotation.z]; + + setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | Types.ArmBotEventsSchema)[]) => [ + ...(prevEvents || []), + newEventData as Types.ArmBotEventsSchema + ]); + + socket.emit("v2:model-asset:add", data); + } else { //REST diff --git a/app/src/modules/scene/controls/selection/moveControls.tsx b/app/src/modules/scene/controls/selection/moveControls.tsx index 5883b62..97ddb17 100644 --- a/app/src/modules/scene/controls/selection/moveControls.tsx +++ b/app/src/modules/scene/controls/selection/moveControls.tsx @@ -323,7 +323,7 @@ function MoveControls({ movedObjects, setMovedObjects, itemsGroupRef, copiedObje rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z }, isLocked: false, isVisible: true, - // eventData: { type: backendEventData.type, points: backendEventData.points }, + eventData: { type: backendEventData.type, points: backendEventData.points }, socketId: socket.id, }; @@ -376,7 +376,7 @@ function MoveControls({ movedObjects, setMovedObjects, itemsGroupRef, copiedObje rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z }, isLocked: false, isVisible: true, - // eventData: { type: backendEventData.type, points: backendEventData.points }, + eventData: { type: backendEventData.type, points: backendEventData.points }, socketId: socket.id, }; diff --git a/app/src/modules/scene/controls/selection/rotateControls.tsx b/app/src/modules/scene/controls/selection/rotateControls.tsx index cf1ac83..0680f28 100644 --- a/app/src/modules/scene/controls/selection/rotateControls.tsx +++ b/app/src/modules/scene/controls/selection/rotateControls.tsx @@ -328,7 +328,7 @@ function RotateControls({ rotatedObjects, setRotatedObjects, movedObjects, setMo rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z }, isLocked: false, isVisible: true, - // eventData: { type: backendEventData.type, points: backendEventData.points }, + eventData: { type: backendEventData.type, points: backendEventData.points }, socketId: socket.id, }; @@ -381,7 +381,7 @@ function RotateControls({ rotatedObjects, setRotatedObjects, movedObjects, setMo rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z }, isLocked: false, isVisible: true, - // eventData: { type: backendEventData.type, points: backendEventData.points }, + eventData: { type: backendEventData.type, points: backendEventData.points }, socketId: socket.id, }; diff --git a/app/src/modules/scene/controls/transformControls.tsx b/app/src/modules/scene/controls/transformControls.tsx index 0f0aaf0..2e586cc 100644 --- a/app/src/modules/scene/controls/transformControls.tsx +++ b/app/src/modules/scene/controls/transformControls.tsx @@ -1,6 +1,6 @@ import { TransformControls } from "@react-three/drei"; import * as THREE from "three"; -import { useselectedFloorItem, useObjectPosition, useObjectScale, useObjectRotation, useTransformMode, useFloorItems, useSocketStore, useActiveTool } from "../../../store/store"; +import { useSelectedFloorItem, useObjectPosition, useObjectScale, useObjectRotation, useTransformMode, useFloorItems, useSocketStore, useActiveTool } from "../../../store/store"; import { useThree } from "@react-three/fiber"; import * as Types from '../../../types/world/worldTypes'; @@ -8,7 +8,7 @@ import { useEffect } from "react"; export default function TransformControl() { const state = useThree(); - const { selectedFloorItem, setselectedFloorItem } = useselectedFloorItem(); + const { selectedFloorItem, setSelectedFloorItem } = useSelectedFloorItem(); const { objectPosition, setObjectPosition } = useObjectPosition(); const { objectScale, setObjectScale } = useObjectScale(); const { objectRotation, setObjectRotation } = useObjectRotation(); @@ -96,7 +96,7 @@ export default function TransformControl() { const target = (state.controls as any).getTarget(new THREE.Vector3()); (state.controls as any).setTarget(target.x, 0, target.z, true); } - setselectedFloorItem(null); + setSelectedFloorItem(null); { setObjectPosition({ x: undefined, y: undefined, z: undefined }); setObjectScale({ x: undefined, y: undefined, z: undefined }); diff --git a/app/src/modules/scene/postProcessing/postProcessing.tsx b/app/src/modules/scene/postProcessing/postProcessing.tsx index 30d8a8f..acae6e3 100644 --- a/app/src/modules/scene/postProcessing/postProcessing.tsx +++ b/app/src/modules/scene/postProcessing/postProcessing.tsx @@ -6,7 +6,7 @@ import { useSelectedActionSphere, useSelectedPath, useSelectedWallItem, - useselectedFloorItem, + useSelectedFloorItem, } from "../../../store/store"; import * as Types from "../../../types/world/worldTypes"; import * as CONSTANTS from "../../../types/world/worldConstants"; @@ -15,7 +15,7 @@ import { useEffect } from "react"; export default function PostProcessing() { const { deletableFloorItem, setDeletableFloorItem } = useDeletableFloorItem(); const { selectedWallItem, setSelectedWallItem } = useSelectedWallItem(); - const { selectedFloorItem, setselectedFloorItem } = useselectedFloorItem(); + const { selectedFloorItem, setSelectedFloorItem } = useSelectedFloorItem(); const { selectedActionSphere } = useSelectedActionSphere(); const { selectedPath } = useSelectedPath(); diff --git a/app/src/modules/scene/scene.tsx b/app/src/modules/scene/scene.tsx index 59273d0..3c1bf78 100644 --- a/app/src/modules/scene/scene.tsx +++ b/app/src/modules/scene/scene.tsx @@ -19,6 +19,7 @@ import Simulation from "../simulation/simulation"; // import Simulation from "./simulationtemp/simulation"; import ZoneCentreTarget from "../../components/ui/componets/zoneCameraTarget"; import Dropped3dWidgets from "../../components/ui/componets/Dropped3dWidget"; +import ZoneAssets from "../../components/ui/componets/zoneAssets"; export default function Scene() { const map = useMemo( @@ -48,6 +49,7 @@ export default function Scene() { + {savedTheme !== "dark" ? : <>} diff --git a/app/src/modules/simulation/path/pathConnector.tsx b/app/src/modules/simulation/path/pathConnector.tsx index 805821c..bb4c039 100644 --- a/app/src/modules/simulation/path/pathConnector.tsx +++ b/app/src/modules/simulation/path/pathConnector.tsx @@ -3,7 +3,7 @@ import React, { useEffect, useRef, useState } from 'react'; import * as THREE from 'three'; import * as Types from '../../../types/world/worldTypes'; import { QuadraticBezierLine } from '@react-three/drei'; -import { useIsConnecting, useRenderDistance, useSimulationStates, useSocketStore } from '../../../store/store'; +import { useDeleteTool, useIsConnecting, useRenderDistance, useSimulationStates, useSocketStore } from '../../../store/store'; import useModuleStore from '../../../store/useModuleStore'; import { usePlayButtonStore } from '../../../store/usePlayButtonStore'; import { setEventApi } from '../../../services/factoryBuilder/assest/floorAsset/setEventsApt'; @@ -11,6 +11,7 @@ import { setEventApi } from '../../../services/factoryBuilder/assest/floorAsset/ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObject }) { const { activeModule } = useModuleStore(); const { gl, raycaster, scene, pointer, camera } = useThree(); + const { deleteTool } = useDeleteTool(); const { renderDistance } = useRenderDistance(); const { setIsConnecting } = useIsConnecting(); const { simulationStates, setSimulationStates } = useSimulationStates(); @@ -21,6 +22,7 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec const [firstSelected, setFirstSelected] = useState<{ modelUUID: string; sphereUUID: string; position: THREE.Vector3; isCorner: boolean; } | null>(null); const [currentLine, setCurrentLine] = useState<{ start: THREE.Vector3, end: THREE.Vector3, mid: THREE.Vector3 } | null>(null); const [helperlineColor, setHelperLineColor] = useState('red'); + const [hoveredLineKey, setHoveredLineKey] = useState(null); const updatePathConnections = (fromModelUUID: string, fromPointUUID: string, toModelUUID: string, toPointUUID: string) => { const updatedPaths = simulationStates.map(path => { @@ -334,7 +336,7 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec path.modeluuid === fromModelUUID || path.modeluuid === toModelUUID ); - // updateBackend(updatedPathDetails); + updateBackend(updatedPathDetails); }; const updateBackend = async (updatedPaths: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | Types.ArmBotEventsSchema)[]) => { @@ -375,6 +377,38 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec socket.emit('v2:model-asset:updateEventData', data); + } else if (updatedPath.type === 'StaticMachine') { + + // await setEventApi( + // organization, + // updatedPath.modeluuid, + // { type: "StaticMachine", points: updatedPath.points } + // ); + + const data = { + organization: organization, + modeluuid: updatedPath.modeluuid, + eventData: { type: "StaticMachine", points: updatedPath.points } + } + + socket.emit('v2:model-asset:updateEventData', data); + + } else if (updatedPath.type === 'ArmBot') { + + // await setEventApi( + // organization, + // updatedPath.modeluuid, + // { type: "ArmBot", points: updatedPath.points } + // ); + + const data = { + organization: organization, + modeluuid: updatedPath.modeluuid, + eventData: { type: "ArmBot", points: updatedPath.points } + } + + socket.emit('v2:model-asset:updateEventData', data); + } }) @@ -479,7 +513,6 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec // For Vehicles, check if they're already connected to anything if (intersected.userData.path.type === 'Vehicle') { - console.log('intersected: ', intersected); const vehicleConnections = intersected.userData.path.points.connections.targets.length; if (vehicleConnections >= 1) { console.log("Vehicle can only have one connection"); @@ -589,20 +622,10 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec } // All checks passed - make the connection - handleAddConnection( - firstSelected.modelUUID, - firstSelected.sphereUUID, - modelUUID, - sphereUUID - ); + handleAddConnection(firstSelected.modelUUID, firstSelected.sphereUUID, modelUUID, sphereUUID); } else { // First selection - just store it - setFirstSelected({ - modelUUID, - sphereUUID, - position: worldPosition, - isCorner: isStartOrEnd - }); + setFirstSelected({ modelUUID, sphereUUID, position: worldPosition, isCorner: isStartOrEnd }); setIsConnecting(true); } } @@ -615,7 +638,7 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec } }; - if (activeModule === 'simulation') { + if (activeModule === 'simulation' && !deleteTool) { canvasElement.addEventListener("mousedown", onMouseDown); canvasElement.addEventListener("mouseup", onMouseUp); canvasElement.addEventListener("mousemove", onMouseMove); @@ -632,7 +655,7 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec canvasElement.removeEventListener("mousemove", onMouseMove); canvasElement.removeEventListener("contextmenu", onContextMenu); }; - }, [camera, scene, raycaster, firstSelected, simulationStates]); + }, [camera, scene, raycaster, firstSelected, simulationStates, deleteTool]); useFrame(() => { Object.values(groupRefs.current).forEach((group) => { @@ -665,9 +688,7 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec } } - const sphereIntersects = raycaster.intersectObjects(pathsGroupRef.current.children, true).filter((obj) => - obj.object.name.includes("events-sphere") - ); + const sphereIntersects = raycaster.intersectObjects(pathsGroupRef.current.children, true).filter((obj) => obj.object.name.includes("events-sphere")); if (sphereIntersects.length > 0) { const sphere = sphereIntersects[0].object; @@ -682,15 +703,21 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec const isVehicleToVehicle = firstPath?.type === 'Vehicle' && secondPath?.type === 'Vehicle'; // Inside the useFrame hook, where we check for snapped spheres: - const isConnectable = (pathData.type === 'Vehicle' || + const isConnectable = ( + pathData.type === 'Vehicle' || + pathData.type === 'ArmBot' || (pathData.points.length > 0 && ( sphereUUID === pathData.points[0].uuid || - sphereUUID === pathData.points[pathData.points.length - 1].uuid - ))) && + sphereUUID === pathData.points[pathData.points.length - 1].uuid || + (pathData.type === 'Conveyor' && firstPath?.type === 'ArmBot') // Allow ArmBot to connect to middle points + )) + ) && !isVehicleToVehicle && - !(firstPath?.type === 'Conveyor' && + !( + firstPath?.type === 'Conveyor' && pathData.type === 'Conveyor' && - !firstSelected.isCorner); + !firstSelected.isCorner + ); // Check for duplicate connection (regardless of path type) const isDuplicateConnection = simulationStates.some(path => { @@ -825,6 +852,127 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec } }); + const removeConnections = (connection1: { model: string; point: string }, connection2: { model: string; point: string }) => { + const updatedStates = simulationStates.map(state => { + // Handle Conveyor (which has multiple points) + if (state.type === 'Conveyor') { + const updatedConveyor: Types.ConveyorEventsSchema = { + ...state, + points: state.points.map(point => { + // Check if this point is either connection1 or connection2 + if ((state.modeluuid === connection1.model && point.uuid === connection1.point) || + (state.modeluuid === connection2.model && point.uuid === connection2.point)) { + + return { + ...point, + connections: { + ...point.connections, + targets: point.connections.targets.filter(target => { + // Remove the target that matches the other connection + return !( + (target.modelUUID === connection1.model && target.pointUUID === connection1.point) || + (target.modelUUID === connection2.model && target.pointUUID === connection2.point) + ); + }) + } + }; + } + return point; + }) + }; + return updatedConveyor; + } + // Handle Vehicle + else if (state.type === 'Vehicle') { + if ((state.modeluuid === connection1.model && state.points.uuid === connection1.point) || + (state.modeluuid === connection2.model && state.points.uuid === connection2.point)) { + + const updatedVehicle: Types.VehicleEventsSchema = { + ...state, + points: { + ...state.points, + connections: { + ...state.points.connections, + targets: state.points.connections.targets.filter(target => { + return !( + (target.modelUUID === connection1.model && target.pointUUID === connection1.point) || + (target.modelUUID === connection2.model && target.pointUUID === connection2.point) + ); + }) + }, + // Ensure all required Vehicle point properties are included + speed: state.points.speed, + actions: state.points.actions + } + }; + return updatedVehicle; + } + } + // Handle StaticMachine + else if (state.type === 'StaticMachine') { + if ((state.modeluuid === connection1.model && state.points.uuid === connection1.point) || + (state.modeluuid === connection2.model && state.points.uuid === connection2.point)) { + + const updatedStaticMachine: Types.StaticMachineEventsSchema = { + ...state, + points: { + ...state.points, + connections: { + ...state.points.connections, + targets: state.points.connections.targets.filter(target => { + return !( + (target.modelUUID === connection1.model && target.pointUUID === connection1.point) || + (target.modelUUID === connection2.model && target.pointUUID === connection2.point) + ); + }) + }, + // Ensure all required StaticMachine point properties are included + actions: state.points.actions, + triggers: state.points.triggers + } + }; + return updatedStaticMachine; + } + } + // Handle ArmBot + else if (state.type === 'ArmBot') { + if ((state.modeluuid === connection1.model && state.points.uuid === connection1.point) || + (state.modeluuid === connection2.model && state.points.uuid === connection2.point)) { + + const updatedArmBot: Types.ArmBotEventsSchema = { + ...state, + points: { + ...state.points, + connections: { + ...state.points.connections, + targets: state.points.connections.targets.filter(target => { + return !( + (target.modelUUID === connection1.model && target.pointUUID === connection1.point) || + (target.modelUUID === connection2.model && target.pointUUID === connection2.point) + ); + }) + }, + // Ensure all required ArmBot point properties are included + actions: state.points.actions, + triggers: state.points.triggers + } + }; + return updatedArmBot; + } + } + return state; + }); + + const updatedPaths = updatedStates.filter(state => + state.modeluuid === connection1.model || state.modeluuid === connection2.model + ); + + updateBackend(updatedPaths); + + setSimulationStates(updatedStates); + }; + + return ( {simulationStates.flatMap(path => { @@ -832,7 +980,7 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec return path.points.flatMap(point => point.connections.targets.map((target, index) => { const targetPath = simulationStates.find(p => p.modeluuid === target.modelUUID); - if (targetPath?.type === 'Vehicle') return null; + if (targetPath?.type !== 'Conveyor' && targetPath?.type !== 'ArmBot') return null; const fromSphere = pathsGroupRef.current?.getObjectByProperty('uuid', point.uuid); const toSphere = pathsGroupRef.current?.getObjectByProperty('uuid', target.pointUUID); @@ -845,11 +993,7 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec const distance = fromWorldPosition.distanceTo(toWorldPosition); const heightFactor = Math.max(0.5, distance * 0.2); - const midPoint = new THREE.Vector3( - (fromWorldPosition.x + toWorldPosition.x) / 2, - Math.max(fromWorldPosition.y, toWorldPosition.y) + heightFactor, - (fromWorldPosition.z + toWorldPosition.z) / 2 - ); + const midPoint = new THREE.Vector3((fromWorldPosition.x + toWorldPosition.x) / 2, Math.max(fromWorldPosition.y, toWorldPosition.y) + heightFactor, (fromWorldPosition.z + toWorldPosition.z) / 2); return ( setHoveredLineKey(`${point.uuid}-${target.pointUUID}-${index}`)} + onPointerOut={() => setHoveredLineKey(null)} + onClick={() => { + if (deleteTool) { + + const connection1 = { model: path.modeluuid, point: point.uuid } + const connection2 = { model: target.modelUUID, point: target.pointUUID } + + removeConnections(connection1, connection2) + + } + }} + userData={target} /> ); } @@ -884,11 +1047,7 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec const distance = fromWorldPosition.distanceTo(toWorldPosition); const heightFactor = Math.max(0.5, distance * 0.2); - const midPoint = new THREE.Vector3( - (fromWorldPosition.x + toWorldPosition.x) / 2, - Math.max(fromWorldPosition.y, toWorldPosition.y) + heightFactor, - (fromWorldPosition.z + toWorldPosition.z) / 2 - ); + const midPoint = new THREE.Vector3((fromWorldPosition.x + toWorldPosition.x) / 2, Math.max(fromWorldPosition.y, toWorldPosition.y) + heightFactor, (fromWorldPosition.z + toWorldPosition.z) / 2); return ( setHoveredLineKey(`${path.points.uuid}-${target.pointUUID}-${index}`)} + onPointerOut={() => setHoveredLineKey(null)} + onClick={() => { + if (deleteTool) { + + const connection1 = { model: path.modeluuid, point: path.points.uuid } + const connection2 = { model: target.modelUUID, point: target.pointUUID } + + removeConnections(connection1, connection2) + } + }} + userData={target} /> ); } @@ -925,11 +1100,7 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec const distance = fromWorldPosition.distanceTo(toWorldPosition); const heightFactor = Math.max(0.5, distance * 0.2); - const midPoint = new THREE.Vector3( - (fromWorldPosition.x + toWorldPosition.x) / 2, - Math.max(fromWorldPosition.y, toWorldPosition.y) + heightFactor, - (fromWorldPosition.z + toWorldPosition.z) / 2 - ); + const midPoint = new THREE.Vector3((fromWorldPosition.x + toWorldPosition.x) / 2, Math.max(fromWorldPosition.y, toWorldPosition.y) + heightFactor, (fromWorldPosition.z + toWorldPosition.z) / 2); return ( setHoveredLineKey(`${path.points.uuid}-${target.pointUUID}-${index}`)} + onPointerOut={() => setHoveredLineKey(null)} + onClick={() => { + if (deleteTool) { + + const connection1 = { model: path.modeluuid, point: path.points.uuid } + const connection2 = { model: target.modelUUID, point: target.pointUUID } + + removeConnections(connection1, connection2) + + } + }} + userData={target} /> ); } diff --git a/app/src/modules/visualization/handleSaveTemplate.ts b/app/src/modules/visualization/handleSaveTemplate.ts index e5f90ab..f192bec 100644 --- a/app/src/modules/visualization/handleSaveTemplate.ts +++ b/app/src/modules/visualization/handleSaveTemplate.ts @@ -64,7 +64,8 @@ export const handleSaveTemplate = async ({ floatingWidget, widgets3D, }; - + + console.log('newTemplate: ', newTemplate); // Extract organization from email const email = localStorage.getItem("email") || ""; const organization = email.includes("@") diff --git a/app/src/store/store.ts b/app/src/store/store.ts index 89cd1a7..d39e8e1 100644 --- a/app/src/store/store.ts +++ b/app/src/store/store.ts @@ -106,9 +106,9 @@ export const useMenuVisible = create((set: any) => ({ setMenuVisible: (x: any) => set(() => ({ menuVisible: x })), })); -export const useDeleteModels = create((set: any) => ({ - deleteModels: false, - setDeleteModels: (x: any) => set(() => ({ deleteModels: x })), +export const useDeleteTool = create((set: any) => ({ + deleteTool: false, + setDeleteTool: (x: any) => set(() => ({ deleteTool: x })), })); export const useToolMode = create((set: any) => ({ @@ -164,9 +164,9 @@ export const useSelectedWallItem = create((set: any) => ({ setSelectedWallItem: (x: any) => set(() => ({ selectedWallItem: x })), })); -export const useselectedFloorItem = create((set: any) => ({ +export const useSelectedFloorItem = create((set: any) => ({ selectedFloorItem: null, - setselectedFloorItem: (x: any) => set(() => ({ selectedFloorItem: x })), + setSelectedFloorItem: (x: any) => set(() => ({ selectedFloorItem: x })), })); export const useDeletableFloorItem = create((set: any) => ({ @@ -446,3 +446,23 @@ export const useTileDistance = create((set: any) => ({ planeValue: { ...state.planeValue, ...value }, })), })); + +// Define the Asset type +type Asset = { + id: string; + name: string; + position?: [number, number, number]; // Optional: 3D position + rotation?: { x: number; y: number; z: number }; // Optional: Euler rotation +}; + +// Zustand store type +type ZoneAssetState = { + zoneAssetId: Asset | null; + setZoneAssetId: (asset: Asset | null) => void; +}; + +// Zustand store +export const useZoneAssetId = create((set) => ({ + zoneAssetId: null, + setZoneAssetId: (asset) => set({ zoneAssetId: asset }), +})); \ No newline at end of file diff --git a/app/src/store/useZone3DWidgetStore.ts b/app/src/store/useZone3DWidgetStore.ts index 7dcaaeb..187c9ec 100644 --- a/app/src/store/useZone3DWidgetStore.ts +++ b/app/src/store/useZone3DWidgetStore.ts @@ -12,7 +12,9 @@ type ZoneWidgetStore = { zoneWidgetData: Record; setZoneWidgetData: (zoneId: string, widgets: WidgetData[]) => void; addWidget: (zoneId: string, widget: WidgetData) => void; + tempWidget: (zoneId: string, widget: WidgetData) => void; updateWidgetPosition: (zoneId: string, widgetId: string, newPosition: [number, number, number]) => void; + tempWidgetPosition: (zoneId: string, widgetId: string, newPosition: [number, number, number]) => void; updateWidgetRotation: (zoneId: string, widgetId: string, newRotation: [number, number, number]) => void; }; @@ -31,6 +33,13 @@ export const useZoneWidgetStore = create((set) => ({ [zoneId]: [...(state.zoneWidgetData[zoneId] || []), { ...widget, rotation: widget.rotation || [0, 0, 0] }], }, })), + tempWidget: (zoneId: string, widget: WidgetData) => + set((state: ZoneWidgetStore) => ({ + zoneWidgetData: { + ...state.zoneWidgetData, + [zoneId]: [...(state.zoneWidgetData[zoneId] || []), { ...widget, rotation: widget.rotation || [0, 0, 0] }], + }, + })), updateWidgetPosition: (zoneId: string, widgetId: string, newPosition: [number, number, number]) => set((state: ZoneWidgetStore) => { @@ -44,6 +53,18 @@ export const useZoneWidgetStore = create((set) => ({ }, }; }), + tempWidgetPosition: (zoneId: string, widgetId: string, newPosition: [number, number, number]) => + set((state: ZoneWidgetStore) => { + const widgets = state.zoneWidgetData[zoneId] || []; + return { + zoneWidgetData: { + ...state.zoneWidgetData, + [zoneId]: widgets.map((widget: WidgetData) => + widget.id === widgetId ? { ...widget, position: newPosition } : widget + ), + }, + }; + }), updateWidgetRotation: (zoneId: string, widgetId: string, newRotation: [number, number, number]) => set((state: ZoneWidgetStore) => { @@ -59,13 +80,6 @@ export const useZoneWidgetStore = create((set) => ({ }), })); -// export type WidgetData = { -// id: string; -// type: string; -// position: [number, number, number]; -// rotation?: [number, number, number]; -// tempPosition?: [number, number, number]; -// }; interface RightClickStore { rightClickSelected: string | null; diff --git a/app/src/styles/layout/sidebar.scss b/app/src/styles/layout/sidebar.scss index 465af2c..3b14aac 100644 --- a/app/src/styles/layout/sidebar.scss +++ b/app/src/styles/layout/sidebar.scss @@ -70,6 +70,67 @@ position: relative; overflow: auto; + .template-list { + display: flex; + flex-direction: column; + gap: 1rem; + padding: 1rem; + min-height: 50vh; + max-height: 60vh; + } + + .template-item { + border: 1px solid #e0e0e0; + border-radius: 8px; + padding: 1rem; + transition: box-shadow 0.3s ease; + } + + .template-image-container { + position: relative; + padding-bottom: 56.25%; // 16:9 aspect ratio + } + + .template-image { + position: absolute; + width: 100%; + height: 100%; + object-fit: contain; + border-radius: 4px; + cursor: pointer; + transition: transform 0.3s ease; + } + + .template-details { + display: flex; + justify-content: space-between; + align-items: center; + margin-top: 0.5rem; + } + + .template-name { + cursor: pointer; + font-weight: 500; + } + + .delete-button { + padding: 0.25rem 0.5rem; + background: #ff4444; + color: white; + border: none; + border-radius: 4px; + cursor: pointer; + transition: opacity 0.3s ease; + } + + .no-templates { + text-align: center; + color: #666; + padding: 2rem; + grid-column: 1 / -1; + } + + .widget-left-sideBar { min-height: 50vh; max-height: 60vh; diff --git a/app/src/styles/pages/realTimeViz.scss b/app/src/styles/pages/realTimeViz.scss index c2c0b7b..0af1201 100644 --- a/app/src/styles/pages/realTimeViz.scss +++ b/app/src/styles/pages/realTimeViz.scss @@ -24,9 +24,17 @@ } .floating { - width: 100%; - max-width: 250px; - min-height: 83px; + + + width: calc(var(--realTimeViz-container-width) * 0.2); + height: calc(var(--realTimeViz-container-width) * 0.05); + + min-width: 250px; + max-width: 300px; + + min-height: 83px !important; + // max-height: 100px !important; + background: var(--background-color); border: 1.23px solid var(--border-color); box-shadow: 0px 4.91px 4.91px 0px #0000001c; @@ -60,9 +68,8 @@ display: flex; background-color: var(--background-color); position: absolute; - bottom: 10px; + // bottom: 10px; left: 50%; - transform: translate(-50%, 0); gap: 6px; border-radius: 8px; @@ -70,6 +77,7 @@ overflow: auto; max-width: calc(100% - 500px); z-index: 3; + transform: translate(-50%, -100%); &::-webkit-scrollbar { display: none; @@ -116,8 +124,8 @@ } .zone-wrapper.bottom { - bottom: calc(var(--realTimeViz-container-height) * 0.27); - bottom: 200px; + top: var(--bottomWidth); + // bottom: 200px; } .content-container { @@ -138,7 +146,7 @@ display: flex; background-color: rgba(224, 223, 255, 0.5); position: absolute; - bottom: 10px; + // bottom: 10px; left: 50%; transform: translate(-50%, 0); gap: 6px; @@ -203,9 +211,9 @@ .chart-container { width: 100%; - min-height: 150px; + max-height: 100%; - // border: 1px dashed var(--background-color-gray); + border: 1px dashed var(--background-color-gray); border-radius: 8px; box-shadow: var(--box-shadow-medium); padding: 6px 0; @@ -343,7 +351,7 @@ .chart-container { width: 100%; - min-height: 160px; + min-height: 150px; max-height: 100%; border-radius: 8px; box-shadow: var(--box-shadow-medium); @@ -362,7 +370,7 @@ .playingFlase { .zone-wrapper.bottom { - bottom: calc(var(--realTimeViz-container-height) * 0.25); + top: var(--bottomWidth); // bottom: 210px; } } @@ -742,7 +750,7 @@ } .activeChart { - outline: 1px solid var(--accent-color); + outline: 2px solid var(--accent-color); z-index: 2 !important; } @@ -786,4 +794,4 @@ } } } -} +} \ No newline at end of file diff --git a/app/src/styles/scene/scene.scss b/app/src/styles/scene/scene.scss index 9de35db..8fdb90f 100644 --- a/app/src/styles/scene/scene.scss +++ b/app/src/styles/scene/scene.scss @@ -21,3 +21,7 @@ box-shadow: var(--box-shadow-light); } } + +.pointer-none{ + pointer-events: none; +} diff --git a/app/src/types/world/worldTypes.d.ts b/app/src/types/world/worldTypes.d.ts index fce09b1..25ef4b6 100644 --- a/app/src/types/world/worldTypes.d.ts +++ b/app/src/types/world/worldTypes.d.ts @@ -324,7 +324,7 @@ interface StaticMachineEventsSchema { points: { uuid: string; position: [number, number, number]; - actions: { uuid: string; name: string; buffer: number | string; material: string; isUsed: boolean }; + actions: { uuid: string; name: string; buffer: number; material: string; }; triggers: { uuid: string; name: string; type: string }; connections: { source: { modelUUID: string; pointUUID: string }; targets: { modelUUID: string; pointUUID: string }[] }; }; @@ -375,5 +375,23 @@ export type EventData = { connections: { source: { modelUUID: string; pointUUID: string }; targets: { modelUUID: string; pointUUID: string }[] }; speed: number; }; + } | { + type: 'StaticMachine'; + points: { + uuid: string; + position: [number, number, number]; + actions: { uuid: string; name: string; buffer: number; material: string; }; + triggers: { uuid: string; name: string; type: string }; + connections: { source: { modelUUID: string; pointUUID: string }; targets: { modelUUID: string; pointUUID: string }[] }; + }; + } | { + type: 'ArmBot'; + points: { + uuid: string; + position: [number, number, number]; + actions: { uuid: string; name: string; speed: number; processes: { triggerId: string; startPoint: string; endPoint: string }[] }; + triggers: { uuid: string; name: string; type: string }; + connections: { source: { modelUUID: string; pointUUID: string }; targets: { modelUUID: string; pointUUID: string }[] }; + }; }; } diff --git a/app/src/utils/outerClick.ts b/app/src/utils/outerClick.ts index de8f7ef..280b3f0 100644 --- a/app/src/utils/outerClick.ts +++ b/app/src/utils/outerClick.ts @@ -1,7 +1,7 @@ import React from "react"; interface OuterClickProps { - contextClassName: string; + contextClassName: string[]; // Make sure this is an array of strings setMenuVisible: React.Dispatch>; } @@ -11,8 +11,12 @@ export default function OuterClick({ }: OuterClickProps) { const handleClick = (event: MouseEvent) => { const targets = event.target as HTMLElement; - // Check if the click is outside the selectable-dropdown-wrapper - if (!targets.closest(`.${contextClassName}`)) { + // Check if the click is outside of any of the provided class names + const isOutside = contextClassName.every( + (className) => !targets.closest(`.${className}`) + ); + + if (isOutside) { setMenuVisible(false); // Close the menu by updating the state } }; @@ -23,7 +27,7 @@ export default function OuterClick({ return () => { document.removeEventListener("click", handleClick); }; - }, []); + }, [contextClassName]); // Add contextClassName to dependency array to handle any changes return null; // This component doesn't render anything }