diff --git a/app/src/components/layout/3D-cards/cards/ProductionCapacity.tsx b/app/src/components/layout/3D-cards/cards/ProductionCapacity.tsx index 4ec9fdd..a719023 100644 --- a/app/src/components/layout/3D-cards/cards/ProductionCapacity.tsx +++ b/app/src/components/layout/3D-cards/cards/ProductionCapacity.tsx @@ -30,14 +30,13 @@ interface ProductionCapacityProps { id: string; type: string; position: [number, number, number]; + rotation: [number, number, number]; onContextMenu?: (event: React.MouseEvent) => void; // onPointerDown:any } -const ProductionCapacity: React.FC = ({ id, type, position, onContextMenu }) => { - +const ProductionCapacity: React.FC = ({ id, type, position, rotation, onContextMenu }) => { const { selectedChartId, setSelectedChartId } = useWidgetStore(); - const { measurements: chartMeasurements, duration: chartDuration, name: widgetName } = useChartStore(); const [measurements, setmeasurements] = useState({}); const [duration, setDuration] = useState("1h") @@ -158,10 +157,10 @@ const ProductionCapacity: React.FC = ({ id, type, posit setDuration(response.data.Data.duration) setName(response.data.widgetName) } else { - console.log("Unexpected response:", response); + } } catch (error) { - console.error("There was an error!", error); + } } } @@ -177,15 +176,42 @@ const ProductionCapacity: React.FC = ({ id, type, posit } , [chartMeasurements, chartDuration, widgetName]) + useEffect(() => { + + + }, [rotation]) + const rotationDegrees = { + x: (rotation[0] * 180) / Math.PI, + y: (rotation[1] * 180) / Math.PI, + z: (rotation[2] * 180) / Math.PI, + }; + + const transformStyle = { + transform: `rotateX(${rotationDegrees.x}deg) rotateY(${rotationDegrees.y}deg) rotateZ(${rotationDegrees.z}deg)`, + }; + return ( - + sprite + zIndexRange={[1,0]} + // center + // distanceFactor={10} // Adjusted for visual balance + style={{ + transform: transformStyle.transform, + transformStyle: 'preserve-3d', + transition: 'transform 0.1s ease-out' + }}>
setSelectedChartId({ id: id, type: type })} onContextMenu={onContextMenu} + style={{ + width: '300px', // Original width + height: '300px', // Original height + transform: transformStyle.transform, + transformStyle: 'preserve-3d' + }} >
Production Capacity
diff --git a/app/src/components/layout/3D-cards/cards/ReturnOfInvestment.tsx b/app/src/components/layout/3D-cards/cards/ReturnOfInvestment.tsx index 96fc305..8e6c707 100644 --- a/app/src/components/layout/3D-cards/cards/ReturnOfInvestment.tsx +++ b/app/src/components/layout/3D-cards/cards/ReturnOfInvestment.tsx @@ -43,9 +43,10 @@ interface ReturnOfInvestmentProps { id: string; type: string; position: [number, number, number]; + rotation: [number, number, number]; onContextMenu?: (event: React.MouseEvent) => void; } -const ReturnOfInvestment: React.FC = ({ id, type, position, onContextMenu }) => { +const ReturnOfInvestment: React.FC = ({ id, type, position, rotation, onContextMenu }) => { const { selectedChartId, setSelectedChartId } = useWidgetStore(); const { measurements: chartMeasurements, duration: chartDuration, name: widgetName } = useChartStore(); @@ -203,13 +204,29 @@ const ReturnOfInvestment: React.FC = ({ id, type, posit } } , [chartMeasurements, chartDuration, widgetName]) + const rotationDegrees = { + x: (rotation[0] * 180) / Math.PI, + y: (rotation[1] * 180) / Math.PI, + z: (rotation[2] * 180) / Math.PI, + }; + + const transformStyle = { + transform: `rotateX(${rotationDegrees.x}deg) rotateY(${rotationDegrees.y}deg) rotateZ(${rotationDegrees.z}deg)`, + }; return ( + sprite + style={{ + transform: transformStyle.transform, + transformStyle: 'preserve-3d', + transition: 'transform 0.1s ease-out' + + }} + >
setSelectedChartId({ id: id, type: type })} onContextMenu={onContextMenu} diff --git a/app/src/components/layout/3D-cards/cards/StateWorking.tsx b/app/src/components/layout/3D-cards/cards/StateWorking.tsx index efd4e7d..287bc15 100644 --- a/app/src/components/layout/3D-cards/cards/StateWorking.tsx +++ b/app/src/components/layout/3D-cards/cards/StateWorking.tsx @@ -10,9 +10,10 @@ interface StateWorkingProps { id: string; type: string; position: [number, number, number]; + rotation: [number, number, number]; onContextMenu?: (event: React.MouseEvent) => void; } -const StateWorking: React.FC = ({ id, type, position, onContextMenu }) => { +const StateWorking: React.FC = ({ id, type, position, rotation, onContextMenu }) => { const { selectedChartId, setSelectedChartId } = useWidgetStore(); const { measurements: chartMeasurements, duration: chartDuration, name: widgetName } = useChartStore(); const [measurements, setmeasurements] = useState({}); @@ -89,13 +90,28 @@ const StateWorking: React.FC = ({ id, type, position, onConte } , [chartMeasurements, chartDuration, widgetName]) + const rotationDegrees = { + x: (rotation[0] * 180) / Math.PI, + y: (rotation[1] * 180) / Math.PI, + z: (rotation[2] * 180) / Math.PI, + }; + const transformStyle = { + transform: `rotateX(${rotationDegrees.x}deg) rotateY(${rotationDegrees.y}deg) rotateZ(${rotationDegrees.z}deg)`, + }; return ( + sprite + style={{ + transform: transformStyle.transform, + transformStyle: 'preserve-3d', + transition: 'transform 0.1s ease-out' + + }} + >
setSelectedChartId({ id: id, type: type })} onContextMenu={onContextMenu} diff --git a/app/src/components/layout/3D-cards/cards/Throughput.tsx b/app/src/components/layout/3D-cards/cards/Throughput.tsx index 5d6fab9..de3109b 100644 --- a/app/src/components/layout/3D-cards/cards/Throughput.tsx +++ b/app/src/components/layout/3D-cards/cards/Throughput.tsx @@ -45,10 +45,11 @@ interface ThroughputProps { id: string; type: string; position: [number, number, number]; + rotation: [number, number, number]; onContextMenu?: (event: React.MouseEvent) => void; } -const Throughput: React.FC = ({ id, type, position, onContextMenu }) => { +const Throughput: React.FC = ({ id, type, position, rotation, onContextMenu }) => { const { selectedChartId, setSelectedChartId } = useWidgetStore(); const { measurements: chartMeasurements, duration: chartDuration, name: widgetName } = useChartStore(); @@ -183,13 +184,29 @@ const Throughput: React.FC = ({ id, type, position, onContextMe } } , [chartMeasurements, chartDuration, widgetName]) + const rotationDegrees = { + x: (rotation[0] * 180) / Math.PI, + y: (rotation[1] * 180) / Math.PI, + z: (rotation[2] * 180) / Math.PI, + }; + + const transformStyle = { + transform: `rotateX(${rotationDegrees.x}deg) rotateY(${rotationDegrees.y}deg) rotateZ(${rotationDegrees.z}deg)`, + }; return ( + sprite + style={{ + transform: transformStyle.transform, + transformStyle: 'preserve-3d', + transition: 'transform 0.1s ease-out' + + }} + >
setSelectedChartId({ id: id, type: type })} onContextMenu={onContextMenu} diff --git a/app/src/components/layout/sidebarRight/properties/GlobalProperties.tsx b/app/src/components/layout/sidebarRight/properties/GlobalProperties.tsx index adae69e..4ffea3b 100644 --- a/app/src/components/layout/sidebarRight/properties/GlobalProperties.tsx +++ b/app/src/components/layout/sidebarRight/properties/GlobalProperties.tsx @@ -33,7 +33,7 @@ const GlobalProperties: React.FC = () => { const { setPlaneValue, setGridValue, planeValue, gridValue } = useTileDistance(); useEffect(() => { - // console.log(gridValue, planeValue, "values"); + }, [gridValue, planeValue]); const { socket } = useSocketStore(); const { limitDistance, setLimitDistance } = useLimitDistance(); diff --git a/app/src/components/layout/sidebarRight/visualization/IotInputCards/FleetEfficiencyInputComponent.tsx b/app/src/components/layout/sidebarRight/visualization/IotInputCards/FleetEfficiencyInputComponent.tsx index cd60d24..55ae422 100644 --- a/app/src/components/layout/sidebarRight/visualization/IotInputCards/FleetEfficiencyInputComponent.tsx +++ b/app/src/components/layout/sidebarRight/visualization/IotInputCards/FleetEfficiencyInputComponent.tsx @@ -45,7 +45,7 @@ const FleetEfficiencyInputComponent = (props: Props) => { try { const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/A_floatWidget/${selectedChartId.id}/${organization}`); if (response.status === 200) { - console.log(response.data); + setSelections(response.data.Data.measurements) setDuration(response.data.Data.duration) diff --git a/app/src/components/layout/sidebarRight/visualization/IotInputCards/FlotingWidgetInput.tsx b/app/src/components/layout/sidebarRight/visualization/IotInputCards/FlotingWidgetInput.tsx index e389802..1164a84 100644 --- a/app/src/components/layout/sidebarRight/visualization/IotInputCards/FlotingWidgetInput.tsx +++ b/app/src/components/layout/sidebarRight/visualization/IotInputCards/FlotingWidgetInput.tsx @@ -45,7 +45,7 @@ const FlotingWidgetInput = (props: Props) => { try { const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/A_floatWidget/${selectedChartId.id}/${organization}`); if (response.status === 200) { - console.log(response.data); + setSelections(response.data.Data.measurements) setDuration(response.data.Data.duration) diff --git a/app/src/components/layout/sidebarRight/visualization/IotInputCards/WarehouseThroughputInputComponent.tsx b/app/src/components/layout/sidebarRight/visualization/IotInputCards/WarehouseThroughputInputComponent.tsx index c35211e..d3ed377 100644 --- a/app/src/components/layout/sidebarRight/visualization/IotInputCards/WarehouseThroughputInputComponent.tsx +++ b/app/src/components/layout/sidebarRight/visualization/IotInputCards/WarehouseThroughputInputComponent.tsx @@ -45,7 +45,7 @@ const WarehouseThroughputInputComponent = (props: Props) => { try { const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/A_floatWidget/${selectedChartId.id}/${organization}`); if (response.status === 200) { - console.log(response.data); + setSelections(response.data.Data.measurements) setDuration(response.data.Data.duration) diff --git a/app/src/components/ui/componets/DraggableWidget.tsx b/app/src/components/ui/componets/DraggableWidget.tsx index 41727db..fa7cff8 100644 --- a/app/src/components/ui/componets/DraggableWidget.tsx +++ b/app/src/components/ui/componets/DraggableWidget.tsx @@ -239,14 +239,13 @@ export const DraggableWidget = ({ onReorder(fromIndex, toIndex); // Call the reorder function passed as a prop } }; - console.log("widget.type", widget.type); // useClickOutside(chartWidget, () => { // setSelectedChartId(null); // }); const { isPlaying } = usePlayButtonStore(); - console.log('isPanelHidden: ', isPanelHidden); + return ( <>
([0, 0, 0]); + const mouseStartRef = useRef<{ x: number; y: number }>({ x: 0, y: 0 }); useEffect(() => { if (activeModule !== "visualization") return; @@ -48,16 +58,18 @@ export default function Dropped3dWidgets() { const organization = email?.split("@")[1]?.split(".")[0]; async function get3dWidgetData() { - let result = await get3dWidgetZoneData(selectedZone.zoneId, organization); - + const result = await get3dWidgetZoneData(selectedZone.zoneId, organization); + console.log('result: ', result); setWidgets3D(result); - const formattedWidgets = result.map((widget: any) => ({ + const formattedWidgets = result.map((widget: WidgetData) => ({ id: widget.id, type: widget.type, position: widget.position, + rotation: widget.rotation || [0, 0, 0], })); + setZoneWidgetData(selectedZone.zoneId, formattedWidgets); } @@ -91,28 +103,24 @@ export default function Dropped3dWidgets() { if (intersects.length > 0) { const { x, y, z } = intersects[0].point; - const newWidget = { + const newWidget: WidgetData = { id: generateUniqueId(), type: widgetSelect, - position: [x, y, z] as [number, number, number], + position: [x, y, z], + rotation: [0, 0, 0], }; - let add3dWidget = { + const add3dWidget = { organization: organization, widget: newWidget, zoneId: selectedZone.zoneId - } + }; if (visualizationSocket) { - visualizationSocket.emit("v2:viz-3D-widget:add", add3dWidget) + visualizationSocket.emit("v2:viz-3D-widget:add", add3dWidget); } - // let response = await adding3dWidgets(selectedZone.zoneId, organization, newWidget); - // - - // if (response.message === "Widget created successfully") { addWidget(selectedZone.zoneId, newWidget); - // } } }; @@ -128,63 +136,103 @@ export default function Dropped3dWidgets() { if (!rightClickSelected) return; const email = localStorage.getItem("email") || ""; const organization = email?.split("@")[1]?.split(".")[0]; + if (rightSelect === "Duplicate") { - const widgetToDuplicate = activeZoneWidgets.find(w => w.id === rightClickSelected); - if (!widgetToDuplicate) return; - const newWidget = { - id: generateUniqueId(), - type: widgetToDuplicate.type, - position: [ - widgetToDuplicate.position[0] + 0.5, // Slightly shift position - widgetToDuplicate.position[1], - widgetToDuplicate.position[2] + 0.5, - ] as [number, number, number], - }; - let add3dWidget = { - organization, - widget: newWidget, - zoneId: selectedZone.zoneId - }; - // if (visualizationSocket) { - // visualizationSocket.emit("v2:viz-3D-widget:add", add3dWidget); - // } - addWidget(selectedZone.zoneId, newWidget); - setRightSelect(null); - setRightClickSelected(null); + async function duplicateWidget() { + + + const widgetToDuplicate = activeZoneWidgets.find((w: WidgetData) => w.id === rightClickSelected); + if (!widgetToDuplicate) return; + const newWidget: WidgetData = { + id: generateUniqueId(), + type: widgetToDuplicate.type, + position: [ + widgetToDuplicate.position[0] + 0.5, + widgetToDuplicate.position[1], + widgetToDuplicate.position[2] + 0.5, + ], + rotation: widgetToDuplicate.rotation || [0, 0, 0], + }; + const adding3dWidget = { + organization, + widget: newWidget, + zoneId: selectedZone.zoneId + }; + let response = await adding3dWidgets(selectedZone.zoneId, organization, newWidget) + console.log('response: ', response); + + addWidget(selectedZone.zoneId, newWidget); + setRightSelect(null); + setRightClickSelected(null); + } + duplicateWidget() } + if (rightSelect === "Delete") { - let deleteWidget = { - organization, - widgetId: rightClickSelected, - zoneId: selectedZone.zoneId + const deleteWidgetApi = async () => { + try { + const deleteWidget = { + organization, + widgetId: rightClickSelected, + zoneId: selectedZone.zoneId, + }; + + // Call the API to delete the widget + const response = await delete3dWidgetApi(selectedZone.zoneId, organization, rightClickSelected); + + + // if (response?.success) { + // Remove from state only if API call succeeds + setZoneWidgetData( + selectedZone.zoneId, + activeZoneWidgets.filter((w: WidgetData) => w.id !== rightClickSelected) + ); + // } else { + // console.error("Failed to delete widget:", response?.message); + // } + } catch (error) { + console.error("Error deleting widget:", error); + } finally { + setRightClickSelected(null); + setRightSelect(null); + } }; - // if (visualizationSocket) { - // visualizationSocket.emit("v2:viz-3D-widget:delete", deleteWidget); - // } - setZoneWidgetData(selectedZone.zoneId, activeZoneWidgets.filter(w => w.id !== rightClickSelected)); - setRightClickSelected(null); - setRightSelect(null); - } - if (rightSelect === "Horizontal Move") { + deleteWidgetApi(); } - if (rightSelect === "Vertical Move") { - - } - }, [rightSelect, rightClickSelected]); useEffect(() => { - const handleMouseMove = (event: MouseEvent) => { + + const email = localStorage.getItem("email") || ""; + const organization = email?.split("@")[1]?.split(".")[0]; + const handleMouseDown = (event: MouseEvent) => { if (!rightClickSelected || !rightSelect) return; + if (rightSelect === "RotateX" || rightSelect === "RotateY") { + mouseStartRef.current = { x: event.clientX, y: event.clientY }; - const selectedZone = Object.keys(zoneWidgetData).find(zoneId => - zoneWidgetData[zoneId].some(widget => 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); + if (selectedWidget) { + rotationStartRef.current = selectedWidget.rotation || [0, 0, 0]; + } + } + }; + + const handleMouseMove = (event: MouseEvent) => { + if (!rightClickSelected || !rightSelect) return; + 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 => widget.id === rightClickSelected); + const selectedWidget = zoneWidgetData[selectedZone].find((widget: WidgetData) => widget.id === rightClickSelected); if (!selectedWidget) return; const rect = gl.domElement.getBoundingClientRect(); @@ -194,72 +242,184 @@ export default function Dropped3dWidgets() { raycaster.setFromCamera(mouse, camera); if (rightSelect === "Horizontal Move" && raycaster.ray.intersectPlane(plane.current, planeIntersect.current)) { - - updateWidgetPosition(selectedZone, rightClickSelected, [ + const newPosition: [number, number, number] = [ planeIntersect.current.x, selectedWidget.position[1], planeIntersect.current.z - ]); + ]; + updateWidgetPosition(selectedZone, rightClickSelected, newPosition); + } if (rightSelect === "Vertical Move") { if (raycaster.ray.intersectPlane(verticalPlane.current, planeIntersect.current)) { updateWidgetPosition(selectedZone, rightClickSelected, [ selectedWidget.position[0], - planeIntersect.current.y, // Ensure Y value updates correctly + planeIntersect.current.y, selectedWidget.position[2] ]); - } else { - console.log("No Intersection with Vertical Plane"); } } + + if (rightSelect === "RotateX") { + const deltaX = event.clientX - mouseStartRef.current.x; + const rotationSpeed = 0.03; + const newRotation: [number, number, number] = [ + rotationStartRef.current[0] + deltaX * rotationSpeed, + rotationStartRef.current[1], + rotationStartRef.current[2] + ]; + updateWidgetRotation(selectedZone, rightClickSelected, newRotation); + } + + if (rightSelect === "RotateY") { + const deltaY = event.clientY - mouseStartRef.current.y; + const rotationSpeed = 0.03; + const newRotation: [number, number, number] = [ + rotationStartRef.current[0], + rotationStartRef.current[1] + deltaY * rotationSpeed, + rotationStartRef.current[2] + ]; + updateWidgetRotation(selectedZone, rightClickSelected, newRotation); + } + if (rightSelect === "RotateZ") { + const deltaX = event.movementX; + const rotationSpeed = 0.03; + const currentRotation = selectedWidget.rotation || [0, 0, 0]; + const newRotation: [number, number, number] = [ + currentRotation[0], + currentRotation[1], + currentRotation[2] + deltaX * rotationSpeed + ]; + updateWidgetRotation(selectedZone, rightClickSelected, newRotation); + } + }; const handleMouseUp = () => { + if (!rightClickSelected || !rightSelect) return; - if (rightClickSelected && (rightSelect === "Horizontal Move" || rightSelect === "Vertical Move")) { + const selectedZone = Object.keys(zoneWidgetData).find(zoneId => + zoneWidgetData[zoneId].some(widget => widget.id === rightClickSelected) + ); - setTimeout(() => { - setRightClickSelected(null); - setRightSelect(null); - }, 50); + if (!selectedZone) return; + + 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") { + console.log(`${rightSelect} Completed - Full Position:`, formatValues(selectedWidget.position)); + let lastPosition = formatValues(selectedWidget.position) as [number, number, number]; + + (async () => { + let response = await update3dWidget(selectedZone, organization, rightClickSelected, lastPosition); + console.log('response: ', response); + if (response) { + console.log("Widget position updated in API:", response); + } + })(); } - }; + else if (rightSelect.includes("Rotate")) { + const rotation = selectedWidget.rotation || [0, 0, 0]; + console.log(`${rightSelect} Completed - Full Rotation:`, formatValues(rotation)); + let lastPosition = formatValues(rotation) as [number, number, number]; + console.log('lastPosition: ', lastPosition); - // Attach events to window instead of gl.domElement + (async () => { + let response = await update3dWidgetRotation(selectedZone, organization, rightClickSelected, lastPosition); + console.log('response: ', response); + if (response) { + console.log("Widget position updated in API:", response); + } + })(); + } + + // Reset selection + setTimeout(() => { + setRightClickSelected(null); + setRightSelect(null); + }, 50); + }; + window.addEventListener("mousedown", handleMouseDown); window.addEventListener("mousemove", handleMouseMove); window.addEventListener("mouseup", handleMouseUp); return () => { + window.removeEventListener("mousedown", handleMouseDown); window.removeEventListener("mousemove", handleMouseMove); window.removeEventListener("mouseup", handleMouseUp); }; }, [rightClickSelected, rightSelect, zoneWidgetData, gl]); - - - return ( <> - {activeZoneWidgets.map(({ id, type, position }) => { - const handleRightClick = (event: React.MouseEvent) => { + {activeZoneWidgets.map(({ id, type, position, rotation = [0, 0, 0] }: WidgetData) => { + const handleRightClick = (event: React.MouseEvent, id: string) => { event.preventDefault(); - setRightClickSelected(id) + 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 ; + return ( + handleRightClick(e, id)} + /> + ); case "ui-Widget 2": - return ; + return ( + handleRightClick(e, id)} + /> + ); case "ui-Widget 3": - return ; + return ( + handleRightClick(e, id)} + /> + ); case "ui-Widget 4": - return ; + 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 8cf1f86..465e8e4 100644 --- a/app/src/components/ui/componets/DroppedFloatingWidgets.tsx +++ b/app/src/components/ui/componets/DroppedFloatingWidgets.tsx @@ -112,7 +112,7 @@ const DroppedObjects: React.FC = () => { const zoneEntries = Object.entries(zones); if (zoneEntries.length === 0) return null; const [zoneName, zone] = zoneEntries[0]; - + function handleDuplicate(zoneName: string, index: number) { setOpenKebabId(null); @@ -123,14 +123,14 @@ const DroppedObjects: React.FC = () => { try { const email = localStorage.getItem("email") || ""; const organization = email?.split("@")[1]?.split(".")[0]; - - + + let deleteFloatingWidget = { floatWidgetID: id, organization: organization, zoneId: zone.zoneId } - + if (visualizationSocket) { visualizationSocket.emit("v2:viz-float:delete", deleteFloatingWidget) } @@ -143,7 +143,7 @@ const DroppedObjects: React.FC = () => { // deleteObject(zoneName, id, index); // Call the deleteObject method from the store // } } catch (error) { - + } } @@ -448,7 +448,7 @@ const DroppedObjects: React.FC = () => { // ...zone.objects[draggingIndex.index], // position: boundedPosition, // }); - + let updateFloatingWidget = { organization: organization, @@ -456,7 +456,7 @@ const DroppedObjects: React.FC = () => { ...zone.objects[draggingIndex.index], position: boundedPosition, }, - index:draggingIndex.index, + index: draggingIndex.index, zoneId: zone.zoneId } if (visualizationSocket) { @@ -464,7 +464,7 @@ const DroppedObjects: React.FC = () => { } // if (response.message === "Widget updated successfully") { - + console.log('boundedPosition: ', boundedPosition); updateObjectPosition(zoneName, draggingIndex.index, boundedPosition); // } @@ -479,7 +479,7 @@ const DroppedObjects: React.FC = () => { // animationRef.current = null; // } } catch (error) { - + } finally { // Clean up regardless of success or failure setDraggingIndex(null); @@ -509,43 +509,37 @@ const DroppedObjects: React.FC = () => { {zone.objects.map((obj, index) => (
{ @@ -559,7 +553,7 @@ const DroppedObjects: React.FC = () => { ) : obj.className === "warehouseThroughput floating" ? ( <> - + ) : obj.className === "fleetEfficiency floating" ? ( <> diff --git a/app/src/components/ui/componets/RealTimeVisulization.tsx b/app/src/components/ui/componets/RealTimeVisulization.tsx index 915e3b3..5a47964 100644 --- a/app/src/components/ui/componets/RealTimeVisulization.tsx +++ b/app/src/components/ui/componets/RealTimeVisulization.tsx @@ -7,7 +7,6 @@ import DisplayZone from "./DisplayZone"; import Scene from "../../../modules/scene/scene"; import useModuleStore from "../../../store/useModuleStore"; - import { useDroppedObjectsStore } from "../../../store/useDroppedObjectsStore"; import { useAsset3dWidget, @@ -24,7 +23,8 @@ import RenderOverlay from "../../templates/Overlay"; import ConfirmationPopup from "../../layout/confirmationPopup/ConfirmationPopup"; import DroppedObjects from "./DroppedFloatingWidgets"; import EditWidgetOption from "../menu/EditWidgetOption"; -import { useRightClickSelected } from "../../../store/useZone3DWidgetStore"; +import { useEditWidgetOptionsStore, useRightClickSelected, useRightSelected } from "../../../store/useZone3DWidgetStore"; + type Side = "top" | "bottom" | "left" | "right"; @@ -59,6 +59,8 @@ const RealTimeVisulization: React.FC = () => { const { selectedZone, setSelectedZone } = useSelectedZoneStore(); + const { rightSelect, setRightSelect } = useRightSelected() + const { editWidgetOptions, setEditWidgetOptions } = useEditWidgetOptionsStore() const { rightClickSelected, setRightClickSelected } = useRightClickSelected() const [openConfirmationPopup, setOpenConfirmationPopup] = useState(false); @@ -69,6 +71,7 @@ const RealTimeVisulization: React.FC = () => { const { widgetSubOption, setWidgetSubOption } = useWidgetSubOption(); const { visualizationSocket } = useSocketStore(); + useEffect(() => { async function GetZoneData() { const email = localStorage.getItem("email") || ""; @@ -142,8 +145,8 @@ const RealTimeVisulization: React.FC = () => { const relativeX = event.clientX - canvasRect.left; const relativeY = event.clientY - canvasRect.top; - const newPosition = determinePosition(canvasRect, relativeX, relativeY) - console.log('newPosition: ', newPosition); + const newPosition = determinePosition(canvasRect, relativeX, relativeY); + console.log("newPosition: ", newPosition); const newObject = { ...droppedData, id: generateUniqueId(), @@ -162,12 +165,12 @@ const RealTimeVisulization: React.FC = () => { let addFloatingWidget = { organization: organization, widget: newObject, - zoneId: selectedZone.zoneId - } - console.log('newObject: ', newObject); + zoneId: selectedZone.zoneId, + }; + console.log("newObject: ", newObject); if (visualizationSocket) { - visualizationSocket.emit("v2:viz-float:add", addFloatingWidget) + visualizationSocket.emit("v2:viz-float:add", addFloatingWidget); } // let response = await addingFloatingWidgets( @@ -196,8 +199,47 @@ const RealTimeVisulization: React.FC = () => { })); } catch (error) { } }; + 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]); + + // 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]); - function handleRightClickSel(){} return (
{ >
- {activeModule === "visualization" && selectedZone.zoneName !== "" && } + {activeModule === "visualization" && selectedZone.zoneName !== "" && ( + + )} {activeModule === "visualization" && } - {activeModule === "visualization" && widgetSubOption === "3D" && rightClickSelected && } + {activeModule === "visualization" && + editWidgetOptions && + rightClickSelected && ( + + )} {activeModule === "visualization" && ( <> diff --git a/app/src/components/ui/menu/EditWidgetOption.tsx b/app/src/components/ui/menu/EditWidgetOption.tsx index ce5261a..1c1fa2e 100644 --- a/app/src/components/ui/menu/EditWidgetOption.tsx +++ b/app/src/components/ui/menu/EditWidgetOption.tsx @@ -1,26 +1,48 @@ import React, { useEffect } from "react"; -import { useLeftData, useRightSelected, useTopData } from "../../../store/useZone3DWidgetStore"; +import { + useEditWidgetOptionsStore, + useLeftData, + useRightClickSelected, + useRightSelected, + useTopData, +} from "../../../store/useZone3DWidgetStore"; interface EditWidgetOptionProps { options: string[]; } -const EditWidgetOption: React.FC = ({ options }) => { - const { top, setTop } = useTopData() - const { left, setLeft } = useLeftData() - const { rightSelect, setRightSelect } = useRightSelected() +const EditWidgetOption: React.FC = ({ + options, +}) => { + const { top } = useTopData(); + const { left } = useLeftData(); + const { setRightSelect } = useRightSelected(); + const { setEditWidgetOptions } = useEditWidgetOptionsStore(); useEffect(() => { - console.log('left: ', left); - console.log('top: ', top); - }, [top, left]) + }, [top, left]); return ( -
+
{options.map((option, index) => ( -
setRightSelect(option)}> +
{ + setRightSelect(option); + setEditWidgetOptions(false); + }} + > {option}
))} diff --git a/app/src/components/ui/realTimeVis/charts/BarGraphComponent.tsx b/app/src/components/ui/realTimeVis/charts/BarGraphComponent.tsx index 4876fe4..f6589a2 100644 --- a/app/src/components/ui/realTimeVis/charts/BarGraphComponent.tsx +++ b/app/src/components/ui/realTimeVis/charts/BarGraphComponent.tsx @@ -238,7 +238,7 @@ const BarGraphComponent = ({ }; useEffect(() => { - console.log("titleeeeeeeeeeeeeeeeeee",title); + },[]) // Memoize Theme Colors diff --git a/app/src/components/ui/realTimeVis/charts/DoughnutGraphComponent.tsx b/app/src/components/ui/realTimeVis/charts/DoughnutGraphComponent.tsx index 6eec49e..93c2960 100644 --- a/app/src/components/ui/realTimeVis/charts/DoughnutGraphComponent.tsx +++ b/app/src/components/ui/realTimeVis/charts/DoughnutGraphComponent.tsx @@ -51,7 +51,7 @@ const DoughnutGraphComponent = ({ }; useEffect(() => { - console.log("titleeeeeeeeeeeeeeeeeee",title); + },[]) // Memoize Theme Colors diff --git a/app/src/components/ui/realTimeVis/charts/LineGraphComponent.tsx b/app/src/components/ui/realTimeVis/charts/LineGraphComponent.tsx index bf76add..c7f7252 100644 --- a/app/src/components/ui/realTimeVis/charts/LineGraphComponent.tsx +++ b/app/src/components/ui/realTimeVis/charts/LineGraphComponent.tsx @@ -51,7 +51,7 @@ const LineGraphComponent = ({ }; useEffect(() => { - console.log("titleeeeeeeeeeeeeeeeeee",title); + },[]) // Memoize Theme Colors diff --git a/app/src/components/ui/realTimeVis/charts/PieGraphComponent.tsx b/app/src/components/ui/realTimeVis/charts/PieGraphComponent.tsx index 094b9e7..d7cc0da 100644 --- a/app/src/components/ui/realTimeVis/charts/PieGraphComponent.tsx +++ b/app/src/components/ui/realTimeVis/charts/PieGraphComponent.tsx @@ -237,7 +237,7 @@ const PieChartComponent = ({ }; useEffect(() => { - console.log("titleeeeeeeeeeeeeeeeeee",title); + },[]) // Memoize Theme Colors diff --git a/app/src/components/ui/realTimeVis/charts/PolarAreaGraphComponent.tsx b/app/src/components/ui/realTimeVis/charts/PolarAreaGraphComponent.tsx index 92581c0..fb87080 100644 --- a/app/src/components/ui/realTimeVis/charts/PolarAreaGraphComponent.tsx +++ b/app/src/components/ui/realTimeVis/charts/PolarAreaGraphComponent.tsx @@ -51,7 +51,7 @@ const PolarAreaGraphComponent = ({ }; useEffect(() => { - console.log("titleeeeeeeeeeeeeeeeeee",title); + },[]) // Memoize Theme Colors diff --git a/app/src/services/realTimeVisulization/zoneData/add3dWidget.ts b/app/src/services/realTimeVisulization/zoneData/add3dWidget.ts index 82562b7..401a5bf 100644 --- a/app/src/services/realTimeVisulization/zoneData/add3dWidget.ts +++ b/app/src/services/realTimeVisulization/zoneData/add3dWidget.ts @@ -5,9 +5,6 @@ export const adding3dWidgets = async ( organization: string, widget: {} ) => { - console.log('widget: ', widget); - console.log('organization: ', organization); - console.log('zoneId: ', zoneId); try { const response = await fetch( `${url_Backend_dwinzo}/api/v2/3dwidget/save`, diff --git a/app/src/services/realTimeVisulization/zoneData/delete3dWidget.ts b/app/src/services/realTimeVisulization/zoneData/delete3dWidget.ts new file mode 100644 index 0000000..fe868f1 --- /dev/null +++ b/app/src/services/realTimeVisulization/zoneData/delete3dWidget.ts @@ -0,0 +1,38 @@ +let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`; +// let url_Backend_dwinzo = `http://192.168.0.102:5000`; + +export const delete3dWidgetApi = async ( + zoneId: string, + organization: string, + id: string +) => { + console.log("zoneId: ", zoneId); + console.log("organization: ", organization); + console.log("id: ", id); + + try { + const response = await fetch( + `${url_Backend_dwinzo}/api/v2/widget3D/delete`, + { + method: "PATCH", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ organization, zoneId, id }), + } + ); + + if (!response.ok) { + throw new Error("Failed to delete floating widget in the zone"); + } + + const result = await response.json(); + return result; + } catch (error) { + if (error instanceof Error) { + throw new Error(error.message); + } else { + throw new Error("An unknown error occurred"); + } + } +}; diff --git a/app/src/services/realTimeVisulization/zoneData/update3dWidget.ts b/app/src/services/realTimeVisulization/zoneData/update3dWidget.ts new file mode 100644 index 0000000..553fc68 --- /dev/null +++ b/app/src/services/realTimeVisulization/zoneData/update3dWidget.ts @@ -0,0 +1,81 @@ +let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`; +// let url_Backend_dwinzo = `http://192.168.0.102:5000`; +export const update3dWidget = async ( + zoneId: string, + organization: string, + id: string, + position?: [number, number, number] +) => { + console.log("organization: ", organization); + console.log("zoneId: ", zoneId); + try { + const response = await fetch( + `${url_Backend_dwinzo}/api/v2/modifyPR/widget3D`, + { + method: "PATCH", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + organization, + zoneId, + id, + position, + }), + } + ); + + if (!response.ok) { + throw new Error("Failed to add 3dwidget in the zone"); + } + + const result = await response.json(); + return result; + } catch (error) { + if (error instanceof Error) { + throw new Error(error.message); + } else { + throw new Error("An unknown error occurred"); + } + } +}; + +export const update3dWidgetRotation = async ( + zoneId: string, + organization: string, + id: string, + rotation?: [number, number, number] +) => { + console.log("organization: ", organization); + console.log("zoneId: ", zoneId); + try { + const response = await fetch( + `${url_Backend_dwinzo}/api/v2/modifyPR/widget3D`, + { + method: "PATCH", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + organization, + zoneId, + id, + rotation, + }), + } + ); + + if (!response.ok) { + throw new Error("Failed to add 3dwidget in the zone"); + } + + const result = await response.json(); + return result; + } catch (error) { + if (error instanceof Error) { + throw new Error(error.message); + } else { + throw new Error("An unknown error occurred"); + } + } +}; diff --git a/app/src/store/useZone3DWidgetStore.ts b/app/src/store/useZone3DWidgetStore.ts index 850623f..7dcaaeb 100644 --- a/app/src/store/useZone3DWidgetStore.ts +++ b/app/src/store/useZone3DWidgetStore.ts @@ -1,10 +1,10 @@ - import { create } from "zustand"; type WidgetData = { id: string; type: string; position: [number, number, number]; + rotation?: [number, number, number]; tempPosition?: [number, number, number]; }; @@ -13,38 +13,59 @@ type ZoneWidgetStore = { setZoneWidgetData: (zoneId: string, widgets: WidgetData[]) => void; addWidget: (zoneId: string, widget: WidgetData) => void; updateWidgetPosition: (zoneId: string, widgetId: string, newPosition: [number, number, number]) => void; + updateWidgetRotation: (zoneId: string, widgetId: string, newRotation: [number, number, number]) => void; }; export const useZoneWidgetStore = create((set) => ({ zoneWidgetData: {}, - setZoneWidgetData: (zoneId, widgets) => - set((state) => ({ + setZoneWidgetData: (zoneId: string, widgets: WidgetData[]) => + set((state: ZoneWidgetStore) => ({ zoneWidgetData: { ...state.zoneWidgetData, [zoneId]: widgets }, })), - addWidget: (zoneId, widget) => - set((state) => ({ + addWidget: (zoneId: string, widget: WidgetData) => + set((state: ZoneWidgetStore) => ({ zoneWidgetData: { ...state.zoneWidgetData, - [zoneId]: [...(state.zoneWidgetData[zoneId] || []), widget], + [zoneId]: [...(state.zoneWidgetData[zoneId] || []), { ...widget, rotation: widget.rotation || [0, 0, 0] }], }, })), - updateWidgetPosition: (zoneId, widgetId, newPosition) => - set((state) => { + updateWidgetPosition: (zoneId: string, widgetId: string, newPosition: [number, number, number]) => + set((state: ZoneWidgetStore) => { const widgets = state.zoneWidgetData[zoneId] || []; return { zoneWidgetData: { ...state.zoneWidgetData, - [zoneId]: widgets.map((widget) => + [zoneId]: widgets.map((widget: WidgetData) => widget.id === widgetId ? { ...widget, position: newPosition } : widget ), }, }; }), + + updateWidgetRotation: (zoneId: string, widgetId: string, newRotation: [number, number, number]) => + set((state: ZoneWidgetStore) => { + const widgets = state.zoneWidgetData[zoneId] || []; + return { + zoneWidgetData: { + ...state.zoneWidgetData, + [zoneId]: widgets.map((widget: WidgetData) => + widget.id === widgetId ? { ...widget, rotation: newRotation } : widget + ), + }, + }; + }), })); +// export type WidgetData = { +// id: string; +// type: string; +// position: [number, number, number]; +// rotation?: [number, number, number]; +// tempPosition?: [number, number, number]; +// }; interface RightClickStore { rightClickSelected: string | null; @@ -75,3 +96,13 @@ export const useRightSelected = create((set) => ({ rightSelect: null, // Default state is null setRightSelect: (x) => set({ rightSelect: x }), })); + +interface EditWidgetOptionsStore { + editWidgetOptions: boolean; + setEditWidgetOptions: (value: boolean) => void; +} + +export const useEditWidgetOptionsStore = create((set) => ({ + editWidgetOptions: false, // Initial state + setEditWidgetOptions: (value: boolean) => set({ editWidgetOptions: value }), +})); diff --git a/app/src/styles/pages/realTimeViz.scss b/app/src/styles/pages/realTimeViz.scss index dd24a14..6909081 100644 --- a/app/src/styles/pages/realTimeViz.scss +++ b/app/src/styles/pages/realTimeViz.scss @@ -729,9 +729,9 @@ .editWidgetOptions { position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); + // top: 50%; + // left: 50%; + // transform: translate(-50%, -50%); background-color: var(--background-color); z-index: 3; display: flex; @@ -739,6 +739,7 @@ border-radius: 6px; overflow: hidden; +min-width: 150px; .option { padding: 8px 10px; color: var(--text-color);