diff --git a/app/src/components/layout/confirmationPopup/ConfirmationPopup.tsx b/app/src/components/layout/confirmationPopup/ConfirmationPopup.tsx new file mode 100644 index 0000000..078f27d --- /dev/null +++ b/app/src/components/layout/confirmationPopup/ConfirmationPopup.tsx @@ -0,0 +1,32 @@ +// ConfirmationDialog.tsx +import React from "react"; + +interface ConfirmationPopupProps { + message: string; + onConfirm: () => void; + onCancel: () => void; +} + +const ConfirmationPopup: React.FC = ({ + message, + onConfirm, + onCancel, +}) => { + return ( +
+
+

{message}

+
+
+ OK +
+
+ Cancel +
+
+
+
+ ); +}; + +export default ConfirmationPopup; diff --git a/app/src/components/layout/sidebarRight/visualization/data/Data.tsx b/app/src/components/layout/sidebarRight/visualization/data/Data.tsx index 40e13ef..d41a38f 100644 --- a/app/src/components/layout/sidebarRight/visualization/data/Data.tsx +++ b/app/src/components/layout/sidebarRight/visualization/data/Data.tsx @@ -119,7 +119,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 e7ef8ee..8066914 100644 --- a/app/src/components/ui/componets/DraggableWidget.tsx +++ b/app/src/components/ui/componets/DraggableWidget.tsx @@ -15,6 +15,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"; import { useSocketStore } from "../../../store/store"; type Side = "top" | "bottom" | "left" | "right"; @@ -83,6 +84,8 @@ export const DraggableWidget = ({ } }; + const chartWidget = useRef(null); + const isPanelHidden = hiddenPanels.includes(widget.panel); const deleteSelectedChart = async () => { @@ -118,13 +121,11 @@ export const DraggableWidget = ({ // })); // } } catch (error) { - } finally { setOpenKebabId(null); } }; - const getCurrentWidgetCount = (panel: Side) => selectedZone.widgets.filter((w) => w.panel === panel).length; @@ -162,7 +163,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) => ({ @@ -171,13 +176,11 @@ export const DraggableWidget = ({ })); } } catch (error) { - } finally { setOpenKebabId(null); } }; - const handleKebabClick = (event: React.MouseEvent) => { event.stopPropagation(); if (openKebabId === widget.id) { @@ -227,22 +230,29 @@ export const DraggableWidget = ({ }; console.log("widget.type", widget.type); + // useClickOutside(chartWidget, () => { + // setSelectedChartId(null); + // }); + + console.log('isPanelHidden: ', isPanelHidden); return ( <>
setSelectedChartId(widget)} > {/* Kebab Icon */}
@@ -253,8 +263,9 @@ export const DraggableWidget = ({ {openKebabId === widget.id && (
diff --git a/app/src/components/ui/componets/Dropped3dWidget.tsx b/app/src/components/ui/componets/Dropped3dWidget.tsx index 23d079c..a40b017 100644 --- a/app/src/components/ui/componets/Dropped3dWidget.tsx +++ b/app/src/components/ui/componets/Dropped3dWidget.tsx @@ -1,4 +1,3 @@ - import { useThree } from "@react-three/fiber"; import React, { useState, useEffect } from "react"; import { useAsset3dWidget, useWidgetSubOption } from "../../../store/store"; @@ -16,22 +15,25 @@ import { get3dWidgetZoneData } from "../../../services/realTimeVisulization/zone import { use3DWidget } from "../../../store/useDroppedObjectsStore"; export default function Dropped3dWidgets() { - const { widgetSelect } = useAsset3dWidget(); - const { activeModule } = useModuleStore(); - const { raycaster, gl, scene }: ThreeState = useThree(); - const { widgetSubOption, setWidgetSubOption } = useWidgetSubOption(); - const { selectedZone } = useSelectedZoneStore(); // Get the currently active zone - // 🔥 Store widget data (id, type, position) based on the selected zone - const [zoneWidgetData, setZoneWidgetData] = useState< - Record - >({}); - const { setWidgets3D } = use3DWidget() - useEffect(() => { - if (activeModule !== "visualization") return - if (selectedZone.zoneName === "") return; + const { widgetSelect } = useAsset3dWidget(); + const { activeModule } = useModuleStore(); + const { raycaster, gl, scene }: ThreeState = useThree(); + const { widgetSubOption, setWidgetSubOption } = useWidgetSubOption(); + const { selectedZone } = useSelectedZoneStore(); // Get the currently active zone + // 🔥 Store widget data (id, type, position) based on the selected zone + const [zoneWidgetData, setZoneWidgetData] = useState< + Record< + string, + { id: string; type: string; position: [number, number, number] }[] + > + >({}); + const { setWidgets3D } = use3DWidget(); + useEffect(() => { + if (activeModule !== "visualization") return; + if (selectedZone.zoneName === "") return; - const email = localStorage.getItem("email") || ""; - const organization = email?.split("@")[1]?.split(".")[0]; + const email = localStorage.getItem("email") || ""; + const organization = email?.split("@")[1]?.split(".")[0]; async function get3dWidgetData() { let result = await get3dWidgetZoneData(selectedZone.zoneId, organization); @@ -44,71 +46,94 @@ export default function Dropped3dWidgets() { position: widget.position, })); - setZoneWidgetData((prev) => ({ - ...prev, - [selectedZone.zoneId]: formattedWidgets, - })); - } + setZoneWidgetData((prev) => ({ + ...prev, + [selectedZone.zoneId]: formattedWidgets, + })); + } - get3dWidgetData(); + get3dWidgetData(); + }, [selectedZone.zoneId, activeModule]); + // useEffect(() => { + // // ✅ Set data only for the selected zone, keeping existing state structure + // setZoneWidgetData((prev) => ({ + // ...prev, + // [selectedZone.zoneId]: [ + // { + // "id": "1743322674626-50mucpb1c", + // "type": "ui-Widget 1", + // "position": [120.94655021768133, 4.142360029666558, 124.39283546121099] + // }, + // { + // "id": "1743322682086-je2h9x33v", + // "type": "ui-Widget 2", + // "position": [131.28751045879255, 0.009999999999970264, 133.92059801984362] + // } + // ] + // })); + // }, [selectedZone.zoneId]); // ✅ Only update when the zone changes - }, [selectedZone.zoneId, activeModule]); + useEffect(() => { + if (activeModule !== "visualization") return; + if (widgetSubOption === "Floating") return; + if (selectedZone.zoneName === "") return; + const canvasElement = gl.domElement; + const onDrop = async (event: DragEvent) => { + event.preventDefault(); // Prevent default browser behavior + 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; + 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; - useEffect(() => { - if (activeModule !== "visualization") return; - if (widgetSubOption === "Floating") return; - if (widgetSubOption === "2D") return; - if (selectedZone.zoneName === "") return - const canvasElement = gl.domElement; - const onDrop = async (event: DragEvent) => { - event.preventDefault(); // Prevent default browser behavior - 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; - 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; - - // ✅ Explicitly define position as a tuple - const newWidget: { id: string; type: string; position: [number, number, number] } = { - id: generateUniqueId(), - type: widgetSelect, - position: [x, y, z], // Ensures TypeScript recognizes it as a tuple - }; - - - let response = await adding3dWidgets(selectedZone.zoneId, organization, newWidget) - console.log('response: ', response); - - if (response.message === "Widget created successfully") { - // ✅ Store widgets uniquely for each zone - setZoneWidgetData((prev) => ({ - ...prev, - [selectedZone.zoneId]: [...(prev[selectedZone.zoneId] || []), newWidget], - })); - } - } + // ✅ Explicitly define position as a tuple + const newWidget: { + id: string; + type: string; + position: [number, number, number]; + } = { + id: generateUniqueId(), + type: widgetSelect, + position: [x, y, z], // Ensures TypeScript recognizes it as a tuple }; + let response = await adding3dWidgets( + selectedZone.zoneId, + organization, + newWidget + ); - canvasElement.addEventListener("drop", onDrop); - return () => { - canvasElement.removeEventListener("drop", onDrop); - }; - }, [widgetSelect, activeModule, selectedZone.zoneId, widgetSubOption]); + // ✅ Store widgets uniquely for each zone + setZoneWidgetData((prev) => ({ + ...prev, + [selectedZone.zoneId]: [ + ...(prev[selectedZone.zoneId] || []), + newWidget, + ], + })); + } + }; - // Get widgets for the currently active zone - const activeZoneWidgets = zoneWidgetData[selectedZone.zoneId] || []; - console.log('activeZoneWidgets: ', activeZoneWidgets); + canvasElement.addEventListener("drop", onDrop); + return () => { + canvasElement.removeEventListener("drop", onDrop); + }; + }, [widgetSelect, activeModule, selectedZone.zoneId, widgetSubOption]); + + // Get widgets for the currently active zone + const activeZoneWidgets = zoneWidgetData[selectedZone.zoneId] || []; return ( <> @@ -130,6 +155,3 @@ export default function Dropped3dWidgets() { ); } - - - diff --git a/app/src/components/ui/componets/DroppedFloatingWidgets.tsx b/app/src/components/ui/componets/DroppedFloatingWidgets.tsx index 019b446..56483fa 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,9 @@ 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"; +import { usePlayButtonStore } from "../../../store/usePlayButtonStore"; +import { useSelectedZoneStore } from "../../../store/useZoneStore"; interface DraggingState { zone: string; index: number; @@ -43,19 +45,24 @@ interface DraggingState { }; } const DroppedObjects: React.FC = () => { + const { isPlaying } = usePlayButtonStore(); const zones = useDroppedObjectsStore((state) => state.zones); const [openKebabId, setOpenKebabId] = useState(null); const updateObjectPosition = useDroppedObjectsStore( (state) => state.updateObjectPosition ); + const { setSelectedZone, selectedZone } = useSelectedZoneStore(); + 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 +75,11 @@ const DroppedObjects: React.FC = () => { } | null>(null); // State to track the current position during drag const animationRef = useRef(null); const { activeModule } = useModuleStore(); + const chartWidget = useRef(null); + + // useClickOutside(chartWidget, () => { + // setSelectedChartId(null); + // }); const kebabRef = useRef(null); @@ -100,7 +112,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 } @@ -110,17 +122,13 @@ 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, id, 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; // Prevent dragging when clicking on the kebab menu or its options @@ -167,11 +175,150 @@ const DroppedObjects: React.FC = () => { } setOffset([offsetY, offsetX]); + + // Add native event listeners for smoother tracking + const handlePointerMoveNative = (e: PointerEvent) => { + if (!draggingIndex || !offset) return; + if (isPlaying === true) 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) { + } 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) => { if (!draggingIndex || !offset) return; - + if (isPlaying === true) return; const container = document.getElementById("real-time-vis-canvas"); if (!container) return; @@ -234,6 +381,7 @@ const DroppedObjects: React.FC = () => { const handlePointerUp = async (event: React.PointerEvent) => { try { if (!draggingIndex || !offset) return; + if (isPlaying === true) return; const container = document.getElementById("real-time-vis-canvas"); if (!container) return; @@ -242,11 +390,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; @@ -328,29 +476,48 @@ const DroppedObjects: React.FC = () => { {zone.objects.map((obj, index) => (
handlePointerDown(event, index)} - onClick={() => { - setSelectedChartId(obj) + onPointerDown={(event) => { + setSelectedChartId(obj); + handlePointerDown(event, index); }} > {obj.className === "floating total-card" ? ( @@ -387,10 +554,13 @@ const DroppedObjects: React.FC = () => {
Duplicate
-
{ - event.stopPropagation(); - handleDelete(zoneName, obj.id, index); // Call the delete handler - }}> +
{ + event.stopPropagation(); + handleDelete(zoneName, obj.id, index); // Call the delete handler + }} + >
@@ -403,7 +573,8 @@ const DroppedObjects: React.FC = () => { ))} {/* Render DistanceLines component during drag */} - {draggingIndex !== null && + {isPlaying === false && + draggingIndex !== null && activeEdges !== null && currentPosition !== null && ( { export default DroppedObjects; - +// always place the mousePointer in the same point in the floatingCard even in pointerMove diff --git a/app/src/components/ui/componets/Panel.tsx b/app/src/components/ui/componets/Panel.tsx index 56cdc06..a25630a 100644 --- a/app/src/components/ui/componets/Panel.tsx +++ b/app/src/components/ui/componets/Panel.tsx @@ -25,7 +25,7 @@ interface PanelProps { lockedPanels: Side[]; zoneId: string; zoneViewPortTarget: number[]; - zoneViewPortPosition: number[] + zoneViewPortPosition: number[]; widgets: Widget[]; }; setSelectedZone: React.Dispatch< @@ -37,7 +37,7 @@ interface PanelProps { lockedPanels: Side[]; zoneId: string; zoneViewPortTarget: number[]; - zoneViewPortPosition: number[] + zoneViewPortPosition: number[]; widgets: Widget[]; }> >; @@ -78,8 +78,9 @@ const Panel: React.FC = ({ case "top": case "bottom": return { - width: `calc(100% - ${(leftActive ? panelSize : 0) + (rightActive ? panelSize : 0) - }px)`, + width: `calc(100% - ${ + (leftActive ? panelSize : 0) + (rightActive ? panelSize : 0) + }px)`, height: `${panelSize - 2}px`, left: leftActive ? `${panelSize}px` : "0", right: rightActive ? `${panelSize}px` : "0", @@ -89,8 +90,9 @@ const Panel: React.FC = ({ case "right": return { width: `${panelSize - 2}px`, - height: `calc(100% - ${(topActive ? panelSize : 0) + (bottomActive ? panelSize : 0) - }px)`, + height: `calc(100% - ${ + (topActive ? panelSize : 0) + (bottomActive ? panelSize : 0) + }px)`, top: topActive ? `${panelSize}px` : "0", bottom: bottomActive ? `${panelSize}px` : "0", [side]: "0", @@ -142,7 +144,7 @@ const Panel: React.FC = ({ // while dublicate check this and add const addWidgetToPanel = async (asset: any, panel: Side) => { const email = localStorage.getItem("email") || ""; - const organization = email?.split("@")[1]?.split(".")[0] + const organization = email?.split("@")[1]?.split(".")[0]; const newWidget = { ...asset, id: generateUniqueId(), @@ -207,7 +209,6 @@ const Panel: React.FC = ({ 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 @@ -234,7 +235,9 @@ const Panel: React.FC = ({ {selectedZone.activeSides.map((side) => (
handleDrop(e, side)} onDragOver={(e) => e.preventDefault()} diff --git a/app/src/components/ui/componets/RealTimeVisulization.tsx b/app/src/components/ui/componets/RealTimeVisulization.tsx index 38aa85d..a0bd711 100644 --- a/app/src/components/ui/componets/RealTimeVisulization.tsx +++ b/app/src/components/ui/componets/RealTimeVisulization.tsx @@ -7,7 +7,7 @@ import DisplayZone from "./DisplayZone"; import Scene from "../../../modules/scene/scene"; import useModuleStore from "../../../store/useModuleStore"; -import DroppedObjects from "./DroppedFloatingWidgets"; + import { useDroppedObjectsStore } from "../../../store/useDroppedObjectsStore"; import { useAsset3dWidget, @@ -20,6 +20,9 @@ import { generateUniqueId } from "../../../functions/generateUniqueId"; import { determinePosition } from "./functions/determinePosition"; import { addingFloatingWidgets } from "../../../services/realTimeVisulization/zoneData/addFloatingWidgets"; import SocketRealTimeViz from "../../../modules/visualization/realTimeVizSocket.dev"; +import RenderOverlay from "../../templates/Overlay"; +import ConfirmationPopup from "../../layout/confirmationPopup/ConfirmationPopup"; +import DroppedObjects from "./DroppedFloatingWidgets"; type Side = "top" | "bottom" | "left" | "right"; @@ -53,6 +56,9 @@ const RealTimeVisulization: React.FC = () => { const [zonesData, setZonesData] = useState({}); const { selectedZone, setSelectedZone } = useSelectedZoneStore(); const { zones } = useZones(); + + const [openConfirmationPopup, setOpenConfirmationPopup] = useState(false); + const [floatingWidgets, setFloatingWidgets] = useState< Record >({}); @@ -87,9 +93,7 @@ const RealTimeVisulization: React.FC = () => { {} ); setZonesData(formattedData); - } catch (error) { - - } + } catch (error) {} } GetZoneData(); @@ -142,7 +146,6 @@ const RealTimeVisulization: React.FC = () => { id: generateUniqueId(), position: determinePosition(canvasRect, relativeX, relativeY), }; - console.log('newObject: ', newObject); let response = await addingFloatingWidgets( selectedZone.zoneId, @@ -179,7 +182,7 @@ const RealTimeVisulization: React.FC = () => { ], }, })); - } catch (error) { } + } catch (error) {} }; return ( @@ -193,6 +196,20 @@ const RealTimeVisulization: React.FC = () => { left: isPlaying || activeModule !== "visualization" ? "0%" : "", }} > + {/* + + */} + {openConfirmationPopup && ( + + console.log("confirm")} + onCancel={() => setOpenConfirmationPopup(false)} + /> + + )}
, + callback: () => void +) => { + useEffect(() => { + const handleClickOutside = (event: MouseEvent) => { + if (ref.current && !event.composedPath().includes(ref.current)) { + callback(); + } + }; + + document.addEventListener("mousedown", handleClickOutside); + + return () => { + document.removeEventListener("mousedown", handleClickOutside); + }; + }, [ref, callback]); +}; \ No newline at end of file diff --git a/app/src/components/ui/menu/EditWidgetOption.tsx b/app/src/components/ui/menu/EditWidgetOption.tsx new file mode 100644 index 0000000..9ef67af --- /dev/null +++ b/app/src/components/ui/menu/EditWidgetOption.tsx @@ -0,0 +1,21 @@ +import React from "react"; + +interface EditWidgetOptionProps { + options: string[]; +} + +const EditWidgetOption: React.FC = ({ options }) => { + return ( +
+
+ {options.map((option, index) => ( +
+ {option} +
+ ))} +
+
+ ); +}; + +export default EditWidgetOption; diff --git a/app/src/modules/builder/groups/floorItemsGroup.tsx b/app/src/modules/builder/groups/floorItemsGroup.tsx index 2f2241e..bee47b5 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,318 +30,405 @@ import addAssetModel from "../geomentries/assets/addAssetModel"; import { getFloorAssets } 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); + } + }; - getFloorAssets(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 }); + getFloorAssets(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(() => { - // console.log('floorItems: ', floorItems); - }, [floorItems]) + const onMouseUp = async (evt: any) => { + if (controls) { + (controls as any).enabled = true; + } + if (evt.button === 0) { + isLeftMouseDown = false; + if (drag) return; - 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/pages/Project.tsx b/app/src/pages/Project.tsx index 7aad5cb..54fe61f 100644 --- a/app/src/pages/Project.tsx +++ b/app/src/pages/Project.tsx @@ -1,4 +1,4 @@ -import React, { useEffect } from "react"; +import React, { useEffect, useState } from "react"; import ModuleToggle from "../components/ui/ModuleToggle"; import SideBarLeft from "../components/layout/sidebarLeft/SideBarLeft"; import SideBarRight from "../components/layout/sidebarRight/SideBarRight"; @@ -20,6 +20,8 @@ import { usePlayButtonStore } from "../store/usePlayButtonStore"; import MarketPlace from "../modules/market/MarketPlace"; import LoadingPage from "../components/templates/LoadingPage"; import SimulationPlayer from "../components/ui/simulation/simulationPlayer"; +import RenderOverlay from "../components/templates/Overlay"; +import MenuBar from "../components/ui/menu/menu"; const Project: React.FC = () => { let navigate = useNavigate(); @@ -30,6 +32,7 @@ const Project: React.FC = () => { const { setFloorItems } = useFloorItems(); const { setWallItems } = useWallItems(); const { setZones } = useZones(); + const [openMenu, setOpenMenu] = useState(true); useEffect(() => { setFloorItems([]); @@ -61,6 +64,9 @@ const Project: React.FC = () => { )} + {/* + + */} {activeModule === "market" && } {activeModule !== "market" && } 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/components/confirmationPopUp.scss b/app/src/styles/components/confirmationPopUp.scss new file mode 100644 index 0000000..4bef0ae --- /dev/null +++ b/app/src/styles/components/confirmationPopUp.scss @@ -0,0 +1,43 @@ +.confirmation-overlay { + width: 100vw; + height: 100vh; + background: var(--background-color-secondary); + backdrop-filter: blur(2px); + + .confirmation-modal { + min-width: 35%; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + z-index: 5; + background-color: var(--background-color); + padding: 14px 12px; + border-radius: 6px; + + .buttton-wrapper { + padding-top: 12px; + display: flex; + justify-content: end; + align-items: end; + gap: 12px; + + .confirmation-button { + padding: 6px 10px; + border-radius: 6px; + cursor: pointer; + + &:first-child { + color: var(--accent-color); + } + + &:last-child { + background-color: #ffe3e0; + color: #f65648; + } + + } + + } + } +} \ No newline at end of file diff --git a/app/src/styles/main.scss b/app/src/styles/main.scss index 7ceb752..5e46dd4 100644 --- a/app/src/styles/main.scss +++ b/app/src/styles/main.scss @@ -24,6 +24,7 @@ @use 'components/marketPlace/marketPlace'; @use 'components/simulation/simulation'; @use 'components/menu/menu'; +@use 'components/confirmationPopUp'; // layout @use 'layout/loading'; diff --git a/app/src/styles/pages/realTimeViz.scss b/app/src/styles/pages/realTimeViz.scss index de3567c..e64ec08 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 { @@ -172,6 +172,7 @@ border-radius: 6px; overflow: visible !important; z-index: $z-index-tools; + overflow: auto; .panel-content { position: relative; @@ -320,6 +321,10 @@ bottom: 0; } } + + .panel.hidePanel { + opacity: 0; + } } .playingFlase { @@ -551,8 +556,8 @@ } .kebab { - width: 30px; - height: 30px; + width: 25px; + height: 25px; position: absolute !important; top: 0px; right: 0px; @@ -577,6 +582,12 @@ box-shadow: var(--box-shadow-medium); + .icon { + width: 25px !important; + height: 25px !important; + background-color: transparent; + } + .btn { display: flex; gap: 6px; @@ -608,18 +619,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; } @@ -632,51 +644,112 @@ 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; +} + + +.editWidgetOptions-wrapper { + + height: 100vh; + width: 100vw; +} + +.editWidgetOptions { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + background-color: var(--background-color); + z-index: 3; + display: flex; + flex-direction: column; + border-radius: 6px; + overflow: hidden; + + .option { + padding: 8px 10px; + color: var(--text-color); + cursor: pointer; + + &:hover { + background-color: var(--highlight-accent-color); + color: var(--accent-color); + } + + &:last-child { + color: #f65648; + + &:hover { + background-color: #ffe3e0; + } + } + } } \ No newline at end of file diff --git a/app/src/utils/theme.ts b/app/src/utils/theme.ts index 7bef114..9395129 100644 --- a/app/src/utils/theme.ts +++ b/app/src/utils/theme.ts @@ -18,4 +18,4 @@ export function toggleTheme() { localStorage.setItem('theme', newTheme); } -setTheme(); \ No newline at end of file +setTheme();