diff --git a/app/src/components/layout/sidebarRight/visualization/data/Data.tsx b/app/src/components/layout/sidebarRight/visualization/data/Data.tsx index 2c9b5c6..9d7e2ca 100644 --- a/app/src/components/layout/sidebarRight/visualization/data/Data.tsx +++ b/app/src/components/layout/sidebarRight/visualization/data/Data.tsx @@ -118,7 +118,7 @@ const Data = () => { }; }); }; - console.log("selectedChartId", selectedChartId); + return (
diff --git a/app/src/components/ui/componets/DraggableWidget.tsx b/app/src/components/ui/componets/DraggableWidget.tsx index db8c408..fa7cc32 100644 --- a/app/src/components/ui/componets/DraggableWidget.tsx +++ b/app/src/components/ui/componets/DraggableWidget.tsx @@ -13,6 +13,7 @@ import { import { useEffect, useRef, useState } from "react"; import { duplicateWidgetApi } from "../../../services/realTimeVisulization/zoneData/duplicateWidget"; import { deleteWidgetApi } from "../../../services/realTimeVisulization/zoneData/deleteWidgetApi"; +import { useClickOutside } from "./functions/handleWidgetsOuterClick"; type Side = "top" | "bottom" | "left" | "right"; @@ -79,6 +80,8 @@ export const DraggableWidget = ({ } }; + const chartWidget = useRef(null); + const isPanelHidden = hiddenPanels.includes(widget.panel); const deleteSelectedChart = async () => { @@ -96,13 +99,11 @@ export const DraggableWidget = ({ })); } } catch (error) { - } finally { setOpenKebabId(null); } }; - const getCurrentWidgetCount = (panel: Side) => selectedZone.widgets.filter((w) => w.panel === panel).length; @@ -140,7 +141,11 @@ export const DraggableWidget = ({ id: `${widget.id}-copy-${Date.now()}`, }; - const response = await duplicateWidgetApi(selectedZone.zoneId, organization, duplicatedWidget); + const response = await duplicateWidgetApi( + selectedZone.zoneId, + organization, + duplicatedWidget + ); if (response?.message === "Widget created successfully") { setSelectedZone((prevZone: any) => ({ @@ -149,13 +154,11 @@ export const DraggableWidget = ({ })); } } catch (error) { - } finally { setOpenKebabId(null); } }; - const handleKebabClick = (event: React.MouseEvent) => { event.stopPropagation(); if (openKebabId === widget.id) { @@ -204,13 +207,18 @@ export const DraggableWidget = ({ } }; + useClickOutside(chartWidget, () => { + setSelectedChartId(null); + }); + return ( <>
setSelectedChartId(widget)} > {/* Kebab Icon */}
@@ -230,8 +240,9 @@ export const DraggableWidget = ({ {openKebabId === widget.id && (
@@ -249,7 +260,7 @@ export const DraggableWidget = ({ )} {/* Render charts based on widget type */} - + {widget.type === "progress" && ( )} diff --git a/app/src/components/ui/componets/DroppedFloatingWidgets.tsx b/app/src/components/ui/componets/DroppedFloatingWidgets.tsx index 10269b9..3aebb84 100644 --- a/app/src/components/ui/componets/DroppedFloatingWidgets.tsx +++ b/app/src/components/ui/componets/DroppedFloatingWidgets.tsx @@ -1,4 +1,3 @@ - import { WalletIcon } from "../../icons/3dChartIcons"; import { useEffect, useRef, useState } from "react"; import { @@ -21,6 +20,7 @@ import TotalCardComponent from "../realTimeVis/floating/TotalCardComponent"; import WarehouseThroughputComponent from "../realTimeVis/floating/WarehouseThroughputComponent"; import FleetEfficiencyComponent from "../realTimeVis/floating/FleetEfficiencyComponent"; import { useWidgetStore } from "../../../store/useWidgetStore"; +import { useClickOutside } from "./functions/handleWidgetsOuterClick"; interface DraggingState { zone: string; index: number; @@ -50,12 +50,14 @@ const DroppedObjects: React.FC = () => { ); const deleteObject = useDroppedObjectsStore((state) => state.deleteObject); - const duplicateObject = useDroppedObjectsStore((state) => state.duplicateObject); + const duplicateObject = useDroppedObjectsStore( + (state) => state.duplicateObject + ); const [draggingIndex, setDraggingIndex] = useState( null ); const [offset, setOffset] = useState<[number, number] | null>(null); - const { setSelectedChartId } = useWidgetStore(); + const { selectedChartId, setSelectedChartId } = useWidgetStore(); const [activeEdges, setActiveEdges] = useState<{ vertical: "top" | "bottom"; horizontal: "left" | "right"; @@ -68,6 +70,12 @@ const DroppedObjects: React.FC = () => { } | null>(null); // State to track the current position during drag const animationRef = useRef(null); const { activeModule } = useModuleStore(); + const chartWidgetFloating = useRef(null); + + // useClickOutside(chartWidgetFloating, () => { + + // setSelectedChartId(null); + // }); // Clean up animation frame on unmount useEffect(() => { @@ -83,7 +91,7 @@ const DroppedObjects: React.FC = () => { const [zoneName, zone] = zoneEntries[0]; function handleDuplicate(zoneName: string, index: number) { - setOpenKebabId(null) + setOpenKebabId(null); duplicateObject(zoneName, index); // Call the duplicateObject method from the store } @@ -93,19 +101,27 @@ const DroppedObjects: React.FC = () => { const organization = email?.split("@")[1]?.split(".")[0]; let res = await deleteFloatingWidgetApi(id, organization); - console.log('res: ', res); if (res.message === "FloatingWidget deleted successfully") { deleteObject(zoneName, index); // Call the deleteObject method from the store } - } catch (error) { - console.error("Error deleting floating widget:", error); - } + } catch (error) {} } - const handlePointerDown = (event: React.PointerEvent, index: number) => { + if ( + (event.target as HTMLElement).closest(".kebab-options") || + (event.target as HTMLElement).closest(".kebab") + ) { + return; + } + const obj = zone.objects[index]; + const element = event.currentTarget as HTMLElement; + + // Set pointer capture to ensure we get all move events + element.setPointerCapture(event.pointerId); + const container = document.getElementById("real-time-vis-canvas"); if (!container) return; @@ -145,6 +161,145 @@ const DroppedObjects: React.FC = () => { } setOffset([offsetY, offsetX]); + + // Add native event listeners for smoother tracking + const handlePointerMoveNative = (e: PointerEvent) => { + if (!draggingIndex || !offset) return; + + const rect = container.getBoundingClientRect(); + const relativeX = e.clientX - rect.left; + const relativeY = e.clientY - rect.top; + + // Use requestAnimationFrame for smooth updates + animationRef.current = requestAnimationFrame(() => { + // Dynamically determine the current position strategy + const newPositionStrategy = determinePosition( + rect, + relativeX, + relativeY + ); + const [activeProp1, activeProp2] = + getActiveProperties(newPositionStrategy); + + // Update active edges for distance lines + const vertical = activeProp1 === "top" ? "top" : "bottom"; + const horizontal = activeProp2 === "left" ? "left" : "right"; + setActiveEdges({ vertical, horizontal }); + + // Calculate new position based on the active properties + let newY = 0; + let newX = 0; + + if (activeProp1 === "top") { + newY = relativeY - offset[0]; + } else { + newY = rect.height - (relativeY + offset[0]); + } + + if (activeProp2 === "left") { + newX = relativeX - offset[1]; + } else { + newX = rect.width - (relativeX + offset[1]); + } + + // Apply boundaries + newX = Math.max(0, Math.min(rect.width - 50, newX)); + newY = Math.max(0, Math.min(rect.height - 50, newY)); + + // Create new position object + const newPosition = { + ...newPositionStrategy, + [activeProp1]: newY, + [activeProp2]: newX, + // Clear opposite properties + [activeProp1 === "top" ? "bottom" : "top"]: "auto", + [activeProp2 === "left" ? "right" : "left"]: "auto", + }; + + // Update the current position state for DistanceLines + setCurrentPosition(newPosition); + updateObjectPosition(zoneName, draggingIndex.index, newPosition); + }); + }; + + const handlePointerUpNative = async (e: PointerEvent) => { + // Clean up native event listeners + element.removeEventListener("pointermove", handlePointerMoveNative); + element.removeEventListener("pointerup", handlePointerUpNative); + element.releasePointerCapture(e.pointerId); + + if (!draggingIndex || !offset) return; + + const rect = container.getBoundingClientRect(); + const relativeX = e.clientX - rect.left; + const relativeY = e.clientY - rect.top; + + // Determine final position strategy + const finalPosition = determinePosition(rect, relativeX, relativeY); + const [activeProp1, activeProp2] = getActiveProperties(finalPosition); + + // Calculate final position + let finalY = 0; + let finalX = 0; + + if (activeProp1 === "top") { + finalY = relativeY - offset[0]; + } else { + finalY = rect.height - (relativeY + offset[0]); + } + + if (activeProp2 === "left") { + finalX = relativeX - offset[1]; + } else { + finalX = rect.width - (relativeX + offset[1]); + } + + // Apply boundaries + finalX = Math.max(0, Math.min(rect.width - 50, finalX)); + finalY = Math.max(0, Math.min(rect.height - 50, finalY)); + + const boundedPosition = { + ...finalPosition, + [activeProp1]: finalY, + [activeProp2]: finalX, + [activeProp1 === "top" ? "bottom" : "top"]: "auto", + [activeProp2 === "left" ? "right" : "left"]: "auto", + }; + + try { + // Save to backend + const email = localStorage.getItem("email") || ""; + const organization = email?.split("@")[1]?.split(".")[0]; + const response = await addingFloatingWidgets( + zone.zoneId, + organization, + { + ...zone.objects[draggingIndex.index], + position: boundedPosition, + } + ); + + if (response.message === "Widget updated successfully") { + updateObjectPosition(zoneName, draggingIndex.index, boundedPosition); + } + } catch (error) { + console.error("Error in handlePointerUp:", error); + } finally { + setDraggingIndex(null); + setOffset(null); + setActiveEdges(null); + setCurrentPosition(null); + + if (animationRef.current) { + cancelAnimationFrame(animationRef.current); + animationRef.current = null; + } + } + }; + + // Add native event listeners + element.addEventListener("pointermove", handlePointerMoveNative); + element.addEventListener("pointerup", handlePointerUpNative); }; const handlePointerMove = (event: React.PointerEvent) => { @@ -199,12 +354,8 @@ const DroppedObjects: React.FC = () => { // Update the current position state for DistanceLines setCurrentPosition(newPosition); - if (!animationRef.current) { - animationRef.current = requestAnimationFrame(() => { - updateObjectPosition(zoneName, draggingIndex.index, newPosition); - animationRef.current = null; - }); - } + // Update position immediately without animation frame + updateObjectPosition(zoneName, draggingIndex.index, newPosition); }; const handlePointerUp = async (event: React.PointerEvent) => { @@ -218,11 +369,11 @@ const DroppedObjects: React.FC = () => { const relativeX = event.clientX - rect.left; const relativeY = event.clientY - rect.top; - // Only now determine the final position strategy + // Determine final position strategy const finalPosition = determinePosition(rect, relativeX, relativeY); const [activeProp1, activeProp2] = getActiveProperties(finalPosition); - // Calculate final position using the new strategy + // Calculate final position let finalY = 0; let finalX = 0; @@ -246,6 +397,9 @@ const DroppedObjects: React.FC = () => { ...finalPosition, [activeProp1]: finalY, [activeProp2]: finalX, + // Clear opposite properties + [activeProp1 === "top" ? "bottom" : "top"]: "auto", + [activeProp2 === "left" ? "right" : "left"]: "auto", }; // Save to backend @@ -259,18 +413,20 @@ const DroppedObjects: React.FC = () => { if (response.message === "Widget updated successfully") { updateObjectPosition(zoneName, draggingIndex.index, boundedPosition); } - - // Clean up + } catch (error) { + console.error("Error in handlePointerUp:", error); + } finally { + // Clean up regardless of success or failure setDraggingIndex(null); setOffset(null); - setActiveEdges(null); // Clear active edges - setCurrentPosition(null); // Reset current position + setActiveEdges(null); + setCurrentPosition(null); + + // Cancel any pending animation frame if (animationRef.current) { cancelAnimationFrame(animationRef.current); animationRef.current = null; } - } catch (error) { - console.error("Error in handlePointerUp:", error); } }; @@ -279,66 +435,6 @@ const DroppedObjects: React.FC = () => { setOpenKebabId((prevId) => (prevId === id ? null : id)); }; - const renderObjectContent = (obj: any) => { - switch (obj.className) { - case "floating total-card": - return ( - <> -
-
{obj.header}
-
-
{obj.value}
-
{obj.per}
-
-
-
- -
- - ); - case "warehouseThroughput floating": - return ( - <> -
-

Warehouse Throughput

-

- (+5) more in 2025 -

-
-
- {/* */} -
- - ); - case "fleetEfficiency floating": - return ( - <> -

Fleet Efficiency

-
-
-
-
-
-
-
-
- 0% -
-
{obj.per}%
-
Optimal
-
- 100% -
- - ); - default: - return null; - } - }; - return (
{ {zone.objects.map((obj, index) => (
{ }} onPointerDown={(event) => handlePointerDown(event, index)} onClick={() => { - setSelectedChartId(obj) + setSelectedChartId(obj); }} > {obj.className === "floating total-card" ? ( @@ -394,19 +493,25 @@ const DroppedObjects: React.FC = () => {
{openKebabId === obj.id && (
-
{ - event.stopPropagation(); - handleDuplicate(zoneName, index); // Call the duplicate handler - }}> +
{ + event.stopPropagation(); + handleDuplicate(zoneName, index); // Call the duplicate handler + }} + >
Duplicate
-
{ - event.stopPropagation(); - handleDelete(zoneName, obj.id, index); // Call the delete handler - }}> +
{ + event.stopPropagation(); + handleDelete(zoneName, obj.id, index); // Call the delete handler + }} + >
@@ -450,5 +555,3 @@ const DroppedObjects: React.FC = () => { }; export default DroppedObjects; - - diff --git a/app/src/components/ui/componets/Panel.tsx b/app/src/components/ui/componets/Panel.tsx index 6500119..797c459 100644 --- a/app/src/components/ui/componets/Panel.tsx +++ b/app/src/components/ui/componets/Panel.tsx @@ -264,3 +264,4 @@ const Panel: React.FC = ({ }; export default Panel; + diff --git a/app/src/components/ui/componets/RealTimeVisulization.tsx b/app/src/components/ui/componets/RealTimeVisulization.tsx index f6a041a..9977f95 100644 --- a/app/src/components/ui/componets/RealTimeVisulization.tsx +++ b/app/src/components/ui/componets/RealTimeVisulization.tsx @@ -63,7 +63,6 @@ const RealTimeVisulization: React.FC = () => { const organization = email?.split("@")[1]?.split(".")[0]; try { const response = await getZone2dData(organization); - console.log('response: ', response); if (!Array.isArray(response)) { return; @@ -84,9 +83,7 @@ const RealTimeVisulization: React.FC = () => { {} ); setZonesData(formattedData); - } catch (error) { - - } + } catch (error) {} } GetZoneData(); @@ -137,7 +134,6 @@ const RealTimeVisulization: React.FC = () => { id: generateUniqueId(), position: determinePosition(canvasRect, relativeX, relativeY), }; - console.log('newObject: ', newObject); let response = await addingFloatingWidgets( selectedZone.zoneId, @@ -174,7 +170,7 @@ const RealTimeVisulization: React.FC = () => { ], }, })); - } catch (error) { } + } catch (error) {} }; return ( @@ -201,7 +197,9 @@ const RealTimeVisulization: React.FC = () => { >
- {activeModule === "visualization" && selectedZone.zoneName !== "" && } + {activeModule === "visualization" && selectedZone.zoneName !== "" && ( + + )} {/* */} {activeModule === "visualization" && ( <> diff --git a/app/src/components/ui/componets/functions/determinePosition.ts b/app/src/components/ui/componets/functions/determinePosition.ts index 000e3a1..1d40236 100644 --- a/app/src/components/ui/componets/functions/determinePosition.ts +++ b/app/src/components/ui/componets/functions/determinePosition.ts @@ -87,7 +87,7 @@ export function determinePosition( if (relativeY < centerY) { if (relativeX < centerX) { - console.log("Top-left"); + position = { top: relativeY, left: relativeX, @@ -95,7 +95,7 @@ export function determinePosition( bottom: "auto", }; } else { - console.log("Top-right"); + position = { top: relativeY, right: canvasRect.width - relativeX, @@ -105,7 +105,7 @@ export function determinePosition( } } else { if (relativeX < centerX) { - console.log("Bottom-left"); + position = { bottom: canvasRect.height - relativeY, left: relativeX, @@ -113,7 +113,7 @@ export function determinePosition( top: "auto", }; } else { - console.log("Bottom-right"); + position = { bottom: canvasRect.height - relativeY, right: canvasRect.width - relativeX, diff --git a/app/src/components/ui/componets/functions/handleWidgetsOuterClick.ts b/app/src/components/ui/componets/functions/handleWidgetsOuterClick.ts new file mode 100644 index 0000000..b640e2f --- /dev/null +++ b/app/src/components/ui/componets/functions/handleWidgetsOuterClick.ts @@ -0,0 +1,22 @@ +import { useEffect } from "react"; + +export const useClickOutside = ( + ref: React.RefObject, + callback: () => void +) => { + useEffect(() => { + const handleClickOutside = (event: MouseEvent) => { + if (ref.current && !ref.current.contains(event.target as Node)) { + callback(); + } + }; + + // Add event listener to document + document.addEventListener("mousedown", handleClickOutside); + + // Cleanup event listener on component unmount + return () => { + document.removeEventListener("mousedown", handleClickOutside); + }; + }, [ref, callback]); +}; diff --git a/app/src/modules/builder/groups/floorItemsGroup.tsx b/app/src/modules/builder/groups/floorItemsGroup.tsx index 3b38d01..e17de69 100644 --- a/app/src/modules/builder/groups/floorItemsGroup.tsx +++ b/app/src/modules/builder/groups/floorItemsGroup.tsx @@ -1,10 +1,26 @@ import { useFrame, useThree } from "@react-three/fiber"; -import { useActiveTool, useAsset3dWidget, useCamMode, useDeletableFloorItem, useDeleteModels, useFloorItems, useLoadingProgress, useRenderDistance, useselectedFloorItem, useSelectedItem, useSocketStore, useToggleView, useTransformMode } from "../../../store/store"; +import { + useActiveTool, + useAsset3dWidget, + useCamMode, + useDeletableFloorItem, + useDeleteModels, + useFloorItems, + useLoadingProgress, + useRenderDistance, + useselectedFloorItem, + useSelectedItem, + useSocketStore, + useToggleView, + useTransformMode, +} from "../../../store/store"; import assetVisibility from "../geomentries/assets/assetVisibility"; import { useEffect } from "react"; import * as THREE from "three"; import * as Types from "../../../types/world/worldTypes"; -import assetManager, { cancelOngoingTasks } from "../geomentries/assets/assetManager"; +import assetManager, { + cancelOngoingTasks, +} from "../geomentries/assets/assetManager"; import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader"; import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader"; import DeletableHoveredFloorItems from "../geomentries/assets/deletableHoveredFloorItems"; @@ -14,320 +30,405 @@ import addAssetModel from "../geomentries/assets/addAssetModel"; import { getFloorItems } from "../../../services/factoryBuilder/assest/floorAsset/getFloorItemsApi"; import useModuleStore from "../../../store/useModuleStore"; // import { retrieveGLTF } from "../../../utils/indexDB/idbUtils"; -const assetManagerWorker = new Worker(new URL('../../../services/factoryBuilder/webWorkers/assetManagerWorker.js', import.meta.url)); -const gltfLoaderWorker = new Worker(new URL('../../../services/factoryBuilder/webWorkers/gltfLoaderWorker.js', import.meta.url)); +const assetManagerWorker = new Worker( + new URL( + "../../../services/factoryBuilder/webWorkers/assetManagerWorker.js", + import.meta.url + ) +); +const gltfLoaderWorker = new Worker( + new URL( + "../../../services/factoryBuilder/webWorkers/gltfLoaderWorker.js", + import.meta.url + ) +); -const FloorItemsGroup = ({ itemsGroup, hoveredDeletableFloorItem, AttachedObject, floorGroup, tempLoader, isTempLoader, plane }: any) => { - const state: Types.ThreeState = useThree(); - const { raycaster, controls }: any = state; - const { renderDistance } = useRenderDistance(); - const { toggleView } = useToggleView(); - const { floorItems, setFloorItems } = useFloorItems(); - const { camMode } = useCamMode(); - const { deleteModels } = useDeleteModels(); - const { setDeletableFloorItem } = useDeletableFloorItem(); - const { transformMode } = useTransformMode(); - const { setselectedFloorItem } = useselectedFloorItem(); - const { activeTool } = useActiveTool(); - const { selectedItem, setSelectedItem } = useSelectedItem(); - const { setLoadingProgress } = useLoadingProgress(); - const { activeModule } = useModuleStore(); - const { socket } = useSocketStore(); - const loader = new GLTFLoader(); - const dracoLoader = new DRACOLoader(); +const FloorItemsGroup = ({ + itemsGroup, + hoveredDeletableFloorItem, + AttachedObject, + floorGroup, + tempLoader, + isTempLoader, + plane, +}: any) => { + const state: Types.ThreeState = useThree(); + const { raycaster, controls }: any = state; + const { renderDistance } = useRenderDistance(); + const { toggleView } = useToggleView(); + const { floorItems, setFloorItems } = useFloorItems(); + const { camMode } = useCamMode(); + const { deleteModels } = useDeleteModels(); + const { setDeletableFloorItem } = useDeletableFloorItem(); + const { transformMode } = useTransformMode(); + const { setselectedFloorItem } = useselectedFloorItem(); + const { activeTool } = useActiveTool(); + const { selectedItem, setSelectedItem } = useSelectedItem(); + const { setLoadingProgress } = useLoadingProgress(); + const { activeModule } = useModuleStore(); + const { socket } = useSocketStore(); + const loader = new GLTFLoader(); + const dracoLoader = new DRACOLoader(); - dracoLoader.setDecoderPath('https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/libs/draco/gltf/'); - loader.setDRACOLoader(dracoLoader); + dracoLoader.setDecoderPath( + "https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/libs/draco/gltf/" + ); + loader.setDRACOLoader(dracoLoader); - useEffect(() => { - const email = localStorage.getItem('email'); - const organization = (email!.split("@")[1]).split(".")[0]; + useEffect(() => { + const email = localStorage.getItem("email"); + const organization = email!.split("@")[1].split(".")[0]; - let totalAssets = 0; - let loadedAssets = 0; + let totalAssets = 0; + let loadedAssets = 0; - const updateLoadingProgress = (progress: number) => { - if (progress < 100) { - setLoadingProgress(progress); - } else if (progress === 100) { - setTimeout(() => { - setLoadingProgress(100); - setTimeout(() => { - setLoadingProgress(0); - }, 1500); - }, 1000); - } - }; + const updateLoadingProgress = (progress: number) => { + if (progress < 100) { + setLoadingProgress(progress); + } else if (progress === 100) { + setTimeout(() => { + setLoadingProgress(100); + setTimeout(() => { + setLoadingProgress(0); + }, 1500); + }, 1000); + } + }; - getFloorItems(organization).then((data) => { - console.log('data: ', data); - const uniqueItems = (data as Types.FloorItems).filter((item, index, self) => - index === self.findIndex((t) => t.modelfileID === item.modelfileID) - ); - totalAssets = uniqueItems.length; - if (totalAssets === 0) { - updateLoadingProgress(100); - return; - } - gltfLoaderWorker.postMessage({ floorItems: data }); + getFloorItems(organization).then((data) => { + const uniqueItems = (data as Types.FloorItems).filter( + (item, index, self) => + index === self.findIndex((t) => t.modelfileID === item.modelfileID) + ); + totalAssets = uniqueItems.length; + if (totalAssets === 0) { + updateLoadingProgress(100); + return; + } + gltfLoaderWorker.postMessage({ floorItems: data }); + }); + + gltfLoaderWorker.onmessage = async (event) => { + if (event.data.message === "gltfLoaded" && event.data.modelBlob) { + const blobUrl = URL.createObjectURL(event.data.modelBlob); + + loader.load(blobUrl, (gltf) => { + URL.revokeObjectURL(blobUrl); + THREE.Cache.remove(blobUrl); + THREE.Cache.add(event.data.modelID, gltf); + + loadedAssets++; + const progress = Math.round((loadedAssets / totalAssets) * 100); + updateLoadingProgress(progress); + + if (loadedAssets === totalAssets) { + loadInitialFloorItems(itemsGroup, setFloorItems); + updateLoadingProgress(100); + } }); + } + }; + }, []); - gltfLoaderWorker.onmessage = async (event) => { - if (event.data.message === "gltfLoaded" && event.data.modelBlob) { - const blobUrl = URL.createObjectURL(event.data.modelBlob); + useEffect(() => { + assetManagerWorker.onmessage = async (event) => { + cancelOngoingTasks(); // Cancel the ongoing process + await assetManager(event.data, itemsGroup, loader); + }; + }, [assetManagerWorker]); - loader.load(blobUrl, (gltf) => { - URL.revokeObjectURL(blobUrl); - THREE.Cache.remove(blobUrl); - THREE.Cache.add(event.data.modelID, gltf); + useEffect(() => { + if (toggleView) return; - loadedAssets++; - const progress = Math.round((loadedAssets / totalAssets) * 100); - updateLoadingProgress(progress); + const uuids: string[] = []; + itemsGroup.current?.children.forEach((child: any) => { + uuids.push(child.uuid); + }); + const cameraPosition = state.camera.position; - if (loadedAssets === totalAssets) { - loadInitialFloorItems(itemsGroup, setFloorItems); - updateLoadingProgress(100); - } - }); - } - }; - }, []); + assetManagerWorker.postMessage({ + floorItems, + cameraPosition, + uuids, + renderDistance, + }); + }, [camMode, renderDistance]); - useEffect(() => { - assetManagerWorker.onmessage = async (event) => { - cancelOngoingTasks(); // Cancel the ongoing process - await assetManager(event.data, itemsGroup, loader); - }; - }, [assetManagerWorker]); + useEffect(() => { + const controls: any = state.controls; + const camera: any = state.camera; - useEffect(() => { - if (toggleView) return + if (controls) { + let intervalId: NodeJS.Timeout | null = null; + + const handleChange = () => { + if (toggleView) return; const uuids: string[] = []; itemsGroup.current?.children.forEach((child: any) => { - uuids.push(child.uuid); + uuids.push(child.uuid); }); - const cameraPosition = state.camera.position; + const cameraPosition = camera.position; - assetManagerWorker.postMessage({ floorItems, cameraPosition, uuids, renderDistance }); - }, [camMode, renderDistance]); + assetManagerWorker.postMessage({ + floorItems, + cameraPosition, + uuids, + renderDistance, + }); + }; - useEffect(() => { - const controls: any = state.controls; - const camera: any = state.camera; - - if (controls) { - let intervalId: NodeJS.Timeout | null = null; - - const handleChange = () => { - if (toggleView) return - - const uuids: string[] = []; - itemsGroup.current?.children.forEach((child: any) => { - uuids.push(child.uuid); - }); - const cameraPosition = camera.position; - - assetManagerWorker.postMessage({ floorItems, cameraPosition, uuids, renderDistance }); - }; - - const startInterval = () => { - if (!intervalId) { - intervalId = setInterval(handleChange, 50); - } - }; - - const stopInterval = () => { - handleChange(); - if (intervalId) { - clearInterval(intervalId); - intervalId = null; - } - }; - - controls.addEventListener('rest', handleChange); - controls.addEventListener('rest', stopInterval); - controls.addEventListener('control', startInterval); - controls.addEventListener('controlend', stopInterval); - - return () => { - controls.removeEventListener('rest', handleChange); - controls.removeEventListener('rest', stopInterval); - controls.removeEventListener('control', startInterval); - controls.removeEventListener('controlend', stopInterval); - if (intervalId) { - clearInterval(intervalId); - } - }; + const startInterval = () => { + if (!intervalId) { + intervalId = setInterval(handleChange, 50); } - }, [state.controls, floorItems, toggleView, renderDistance]); + }; - useEffect(() => { - const canvasElement = state.gl.domElement; - let drag = false; - let isLeftMouseDown = false; - - const onMouseDown = (evt: any) => { - if (evt.button === 0) { - isLeftMouseDown = true; - drag = false; - } - }; - - const onMouseMove = () => { - if (isLeftMouseDown) { - drag = true; - } - }; - - const onMouseUp = async (evt: any) => { - if (controls) { - (controls as any).enabled = true; - } - if (evt.button === 0) { - isLeftMouseDown = false; - if (drag) return; - - if (deleteModels) { - DeleteFloorItems(itemsGroup, hoveredDeletableFloorItem, setFloorItems, socket); - } - const Mode = transformMode; - - if (Mode !== null || activeTool === "cursor") { - if (!itemsGroup.current) return; - let intersects = raycaster.intersectObjects(itemsGroup.current.children, true); - if (intersects.length > 0 && intersects[0]?.object?.parent?.parent?.position && intersects[0]?.object?.parent?.parent?.scale && intersects[0]?.object?.parent?.parent?.rotation) { - // let currentObject = intersects[0].object; - - // while (currentObject) { - // if (currentObject.name === "Scene") { - // break; - // } - // currentObject = currentObject.parent as THREE.Object3D; - // } - // if (currentObject) { - // AttachedObject.current = currentObject as any; - // setselectedFloorItem(AttachedObject.current!); - // } - } else { - const target = controls.getTarget(new THREE.Vector3()); - await controls.setTarget(target.x, 0, target.z, true); - setselectedFloorItem(null); - } - } - } - }; - - const onDblClick = async (evt: any) => { - if (evt.button === 0) { - isLeftMouseDown = false; - if (drag) return; - - const Mode = transformMode; - - if (Mode !== null || activeTool === "cursor") { - if (!itemsGroup.current) return; - let intersects = raycaster.intersectObjects(itemsGroup.current.children, true); - if (intersects.length > 0 && intersects[0]?.object?.parent?.parent?.position && intersects[0]?.object?.parent?.parent?.scale && intersects[0]?.object?.parent?.parent?.rotation) { - let currentObject = intersects[0].object; - - while (currentObject) { - if (currentObject.name === "Scene") { - break; - } - currentObject = currentObject.parent as THREE.Object3D; - } - if (currentObject) { - AttachedObject.current = currentObject as any; - // controls.fitToSphere(AttachedObject.current!, true); - - const bbox = new THREE.Box3().setFromObject(AttachedObject.current); - const size = bbox.getSize(new THREE.Vector3()); - const center = bbox.getCenter(new THREE.Vector3()); - - const front = new THREE.Vector3(0, 0, 1); - AttachedObject.current.localToWorld(front); - front.sub(AttachedObject.current.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(AttachedObject.current!, true, { cover: true, paddingTop: 5, paddingLeft: 5, paddingBottom: 5, paddingRight: 5 }); - - setselectedFloorItem(AttachedObject.current!); - } - } else { - const target = controls.getTarget(new THREE.Vector3()); - await controls.setTarget(target.x, 0, target.z, true); - setselectedFloorItem(null); - } - } - } + const stopInterval = () => { + handleChange(); + if (intervalId) { + clearInterval(intervalId); + intervalId = null; } + }; - const onDrop = (event: any) => { + controls.addEventListener("rest", handleChange); + controls.addEventListener("rest", stopInterval); + controls.addEventListener("control", startInterval); + controls.addEventListener("controlend", stopInterval); - if (!event.dataTransfer?.files[0]) return; - - if (selectedItem.id !== "" && event.dataTransfer?.files[0]) { - addAssetModel(raycaster, state.camera, state.pointer, floorGroup, setFloorItems, itemsGroup, isTempLoader, tempLoader, socket, selectedItem, setSelectedItem, plane); - } + return () => { + controls.removeEventListener("rest", handleChange); + controls.removeEventListener("rest", stopInterval); + controls.removeEventListener("control", startInterval); + controls.removeEventListener("controlend", stopInterval); + if (intervalId) { + clearInterval(intervalId); } + }; + } + }, [state.controls, floorItems, toggleView, renderDistance]); - const onDragOver = (event: any) => { - event.preventDefault(); - }; + useEffect(() => { + const canvasElement = state.gl.domElement; + let drag = false; + let isLeftMouseDown = false; - if (activeModule === "builder") { - canvasElement.addEventListener("mousedown", onMouseDown); - canvasElement.addEventListener("mouseup", onMouseUp); - canvasElement.addEventListener("mousemove", onMouseMove); - canvasElement.addEventListener("dblclick", onDblClick); - canvasElement.addEventListener("drop", onDrop); - canvasElement.addEventListener("dragover", onDragOver); - } else { - if (controls) { - const target = controls.getTarget(new THREE.Vector3()); - controls.setTarget(target.x, 0, target.z, true); - setselectedFloorItem(null); - } - } + const onMouseDown = (evt: any) => { + if (evt.button === 0) { + isLeftMouseDown = true; + drag = false; + } + }; - return () => { - canvasElement.removeEventListener("mousedown", onMouseDown); - canvasElement.removeEventListener("mouseup", onMouseUp); - canvasElement.removeEventListener("mousemove", onMouseMove); - canvasElement.removeEventListener("dblclick", onDblClick); - canvasElement.removeEventListener("drop", onDrop); - canvasElement.removeEventListener("dragover", onDragOver); - }; - }, [deleteModels, transformMode, controls, selectedItem, state.camera, state.pointer, activeTool, activeModule]); + const onMouseMove = () => { + if (isLeftMouseDown) { + drag = true; + } + }; - useEffect(() => { + const onMouseUp = async (evt: any) => { + if (controls) { + (controls as any).enabled = true; + } + if (evt.button === 0) { + isLeftMouseDown = false; + if (drag) return; - console.log('floorItems: ', floorItems); - }, [floorItems]) - - useFrame(() => { - if (controls) - assetVisibility(itemsGroup, state.camera.position, renderDistance); if (deleteModels) { - DeletableHoveredFloorItems(state, itemsGroup, hoveredDeletableFloorItem, setDeletableFloorItem); - } else if (!deleteModels) { - if (hoveredDeletableFloorItem.current) { - hoveredDeletableFloorItem.current = undefined; - setDeletableFloorItem(null); - } + DeleteFloorItems( + itemsGroup, + hoveredDeletableFloorItem, + setFloorItems, + socket + ); } - }) + const Mode = transformMode; - return ( - - - ) -} + if (Mode !== null || activeTool === "cursor") { + if (!itemsGroup.current) return; + let intersects = raycaster.intersectObjects( + itemsGroup.current.children, + true + ); + if ( + intersects.length > 0 && + intersects[0]?.object?.parent?.parent?.position && + intersects[0]?.object?.parent?.parent?.scale && + intersects[0]?.object?.parent?.parent?.rotation + ) { + // let currentObject = intersects[0].object; + // while (currentObject) { + // if (currentObject.name === "Scene") { + // break; + // } + // currentObject = currentObject.parent as THREE.Object3D; + // } + // if (currentObject) { + // AttachedObject.current = currentObject as any; + // setselectedFloorItem(AttachedObject.current!); + // } + } else { + const target = controls.getTarget(new THREE.Vector3()); + await controls.setTarget(target.x, 0, target.z, true); + setselectedFloorItem(null); + } + } + } + }; -export default FloorItemsGroup; \ No newline at end of file + const onDblClick = async (evt: any) => { + if (evt.button === 0) { + isLeftMouseDown = false; + if (drag) return; + + const Mode = transformMode; + + if (Mode !== null || activeTool === "cursor") { + if (!itemsGroup.current) return; + let intersects = raycaster.intersectObjects( + itemsGroup.current.children, + true + ); + if ( + intersects.length > 0 && + intersects[0]?.object?.parent?.parent?.position && + intersects[0]?.object?.parent?.parent?.scale && + intersects[0]?.object?.parent?.parent?.rotation + ) { + let currentObject = intersects[0].object; + + while (currentObject) { + if (currentObject.name === "Scene") { + break; + } + currentObject = currentObject.parent as THREE.Object3D; + } + if (currentObject) { + AttachedObject.current = currentObject as any; + // controls.fitToSphere(AttachedObject.current!, true); + + const bbox = new THREE.Box3().setFromObject( + AttachedObject.current + ); + const size = bbox.getSize(new THREE.Vector3()); + const center = bbox.getCenter(new THREE.Vector3()); + + const front = new THREE.Vector3(0, 0, 1); + AttachedObject.current.localToWorld(front); + front.sub(AttachedObject.current.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(AttachedObject.current!, true, { + cover: true, + paddingTop: 5, + paddingLeft: 5, + paddingBottom: 5, + paddingRight: 5, + }); + + setselectedFloorItem(AttachedObject.current!); + } + } else { + const target = controls.getTarget(new THREE.Vector3()); + await controls.setTarget(target.x, 0, target.z, true); + setselectedFloorItem(null); + } + } + } + }; + + const onDrop = (event: any) => { + if (!event.dataTransfer?.files[0]) return; + + if (selectedItem.id !== "" && event.dataTransfer?.files[0]) { + addAssetModel( + raycaster, + state.camera, + state.pointer, + floorGroup, + setFloorItems, + itemsGroup, + isTempLoader, + tempLoader, + socket, + selectedItem, + setSelectedItem, + plane + ); + } + }; + + const onDragOver = (event: any) => { + event.preventDefault(); + }; + + if (activeModule === "builder") { + canvasElement.addEventListener("mousedown", onMouseDown); + canvasElement.addEventListener("mouseup", onMouseUp); + canvasElement.addEventListener("mousemove", onMouseMove); + canvasElement.addEventListener("dblclick", onDblClick); + canvasElement.addEventListener("drop", onDrop); + canvasElement.addEventListener("dragover", onDragOver); + } else { + if (controls) { + const target = controls.getTarget(new THREE.Vector3()); + controls.setTarget(target.x, 0, target.z, true); + setselectedFloorItem(null); + } + } + + return () => { + canvasElement.removeEventListener("mousedown", onMouseDown); + canvasElement.removeEventListener("mouseup", onMouseUp); + canvasElement.removeEventListener("mousemove", onMouseMove); + canvasElement.removeEventListener("dblclick", onDblClick); + canvasElement.removeEventListener("drop", onDrop); + canvasElement.removeEventListener("dragover", onDragOver); + }; + }, [ + deleteModels, + transformMode, + controls, + selectedItem, + state.camera, + state.pointer, + activeTool, + activeModule, + ]); + + useEffect(() => {}, [floorItems]); + + useFrame(() => { + if (controls) + assetVisibility(itemsGroup, state.camera.position, renderDistance); + if (deleteModels) { + DeletableHoveredFloorItems( + state, + itemsGroup, + hoveredDeletableFloorItem, + setDeletableFloorItem + ); + } else if (!deleteModels) { + if (hoveredDeletableFloorItem.current) { + hoveredDeletableFloorItem.current = undefined; + setDeletableFloorItem(null); + } + } + }); + + return ; +}; + +export default FloorItemsGroup; diff --git a/app/src/services/realTimeVisulization/zoneData/addFloatingWidgets.ts b/app/src/services/realTimeVisulization/zoneData/addFloatingWidgets.ts index fb644c6..9b03dbe 100644 --- a/app/src/services/realTimeVisulization/zoneData/addFloatingWidgets.ts +++ b/app/src/services/realTimeVisulization/zoneData/addFloatingWidgets.ts @@ -6,9 +6,9 @@ export const addingFloatingWidgets = async ( organization: string, widget: {} ) => { - console.log('organization: ', organization); - console.log('widget: ', widget); - console.log('zoneId: ', zoneId); + + + try { const response = await fetch( `${url_Backend_dwinzo}/api/v2/floatwidget/save`, diff --git a/app/src/styles/pages/realTimeViz.scss b/app/src/styles/pages/realTimeViz.scss index e4cbcac..ed6d7c2 100644 --- a/app/src/styles/pages/realTimeViz.scss +++ b/app/src/styles/pages/realTimeViz.scss @@ -26,7 +26,7 @@ border-radius: $border-radius-medium; padding: 18px; position: absolute; - z-index: 1000; + z-index: 1; } .scene-container { @@ -544,21 +544,21 @@ } .floating-wrapper { - .icon { - width: 25px !important; - height: 25px !important; - background-color: transparent; - } + // .icon { + // width: 25px !important; + // height: 25px !important; + // } .kebab { - width: 30px; - height: 30px; + width: 25px; + height: 25px; position: absolute !important; top: 0px; right: 0px; z-index: 10; cursor: pointer; @include flex-center; + background-color: transparent; } .kebab-options { @@ -576,6 +576,12 @@ box-shadow: var(--box-shadow-medium); + .icon { + width: 25px !important; + height: 25px !important; + background-color: transparent; + } + .btn { display: flex; gap: 6px; @@ -607,18 +613,19 @@ } } - .dublicate { - cursor: not-allowed; - } + + } } .distance-line { position: absolute; border-style: dashed; - border-color: var(--accent-color); /* Green color for visibility */ + border-color: var(--accent-color); + /* Green color for visibility */ border-width: 1px; - pointer-events: none; /* Ensure lins don't interfere with dragging */ + pointer-events: none; + /* Ensure lins don't interfere with dragging */ z-index: 10000; } @@ -631,51 +638,73 @@ padding: 2px 6px; border-radius: 3px; white-space: nowrap; - transform: translate(-50%, -50%); /* Center the label */ + transform: translate(-50%, -50%); + /* Center the label */ } /* Specific styles for each type of line */ /* Top distance line */ .distance-line.top { - border-bottom: none; /* Remove bottom border for a single line */ - width: 2px; /* Thin vertical line */ + border-bottom: none; + /* Remove bottom border for a single line */ + width: 2px; + /* Thin vertical line */ } .distance-line.top .distance-label { - top: -10px; /* Position label above the line */ - left: 50%; /* Center horizontally */ + top: -10px; + /* Position label above the line */ + left: 50%; + /* Center horizontally */ } /* Bottom distance line */ .distance-line.bottom { - border-top: none; /* Remove top border for a single line */ - width: 2px; /* Thin vertical line */ + border-top: none; + /* Remove top border for a single line */ + width: 2px; + /* Thin vertical line */ } .distance-line.bottom .distance-label { - bottom: -10px; /* Position label below the line */ - left: 50%; /* Center horizontally */ + bottom: -10px; + /* Position label below the line */ + left: 50%; + /* Center horizontally */ } /* Left distance line */ .distance-line.left { - border-right: none; /* Remove right border for a single line */ - height: 2px; /* Thin horizontal line */ + border-right: none; + /* Remove right border for a single line */ + height: 2px; + /* Thin horizontal line */ } .distance-line.left .distance-label { - left: -10px; /* Position label to the left of the line */ - top: 50%; /* Center vertically */ + left: -10px; + /* Position label to the left of the line */ + top: 50%; + /* Center vertically */ } /* Right distance line */ .distance-line.right { - border-left: none; /* Remove left border for a single line */ - height: 2px; /* Thin horizontal line */ + border-left: none; + /* Remove left border for a single line */ + height: 2px; + /* Thin horizontal line */ } .distance-line.right .distance-label { - right: -10px; /* Position label to the right of the line */ - top: 50%; /* Center vertically */ + right: -10px; + /* Position label to the right of the line */ + top: 50%; + /* Center vertically */ +} + +.activeChart { + outline: 1px solid var(--accent-color); + z-index: 2 !important; } \ No newline at end of file