From 70807d4ec4c629ed35b34d6167dbf4ed7b3cfad1 Mon Sep 17 00:00:00 2001 From: Nalvazhuthi Date: Thu, 3 Apr 2025 18:02:28 +0530 Subject: [PATCH 1/4] added zones drop down in builder ouline and adjust width of displayZones --- app/package-lock.json | 98 +++--- app/package.json | 1 + .../icons/RealTimeVisulationIcons.tsx | 22 +- .../layout/sidebarLeft/SideBarLeft.tsx | 13 +- .../visualization/widgets/Widgets2D.tsx | 2 +- .../visualization/design/Design.tsx | 289 ++++++++++++------ app/src/components/ui/componets/Panel.tsx | 4 +- .../ui/componets/RealTimeVisulization.tsx | 2 +- .../componets/functions/determinePosition.ts | 28 +- app/src/components/ui/list/DropDownList.tsx | 79 ++++- app/src/components/ui/list/List.tsx | 128 ++++++-- .../visualization/handleSaveTemplate.ts | 2 +- app/src/pages/Project.tsx | 2 +- .../zoneData/saveTempleteApi.ts | 4 +- app/src/styles/components/lists.scss | 30 +- app/src/styles/layout/sidebar.scss | 20 +- app/src/styles/pages/realTimeViz.scss | 16 +- 17 files changed, 545 insertions(+), 195 deletions(-) diff --git a/app/package-lock.json b/app/package-lock.json index a820896..8be748e 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -29,6 +29,7 @@ "chartjs-plugin-annotation": "^3.1.0", "glob": "^11.0.0", "gsap": "^3.12.5", + "html2canvas": "^1.4.1", "leva": "^0.10.0", "mqtt": "^5.10.4", "postprocessing": "^6.36.4", @@ -2019,7 +2020,7 @@ "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "devOptional": true, + "dev": true, "dependencies": { "@jridgewell/trace-mapping": "0.3.9" }, @@ -2031,7 +2032,7 @@ "version": "0.3.9", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "devOptional": true, + "dev": true, "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" @@ -4134,26 +4135,6 @@ "url": "https://github.com/sponsors/gregberge" } }, - "node_modules/@testing-library/dom": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.0.tgz", - "integrity": "sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==", - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/code-frame": "^7.10.4", - "@babel/runtime": "^7.12.5", - "@types/aria-query": "^5.0.1", - "aria-query": "5.3.0", - "chalk": "^4.1.0", - "dom-accessibility-api": "^0.5.9", - "lz-string": "^1.5.0", - "pretty-format": "^27.0.2" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/@testing-library/jest-dom": { "version": "5.17.0", "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.17.0.tgz", @@ -4265,25 +4246,25 @@ "version": "1.0.11", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", - "devOptional": true + "dev": true }, "node_modules/@tsconfig/node12": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "devOptional": true + "dev": true }, "node_modules/@tsconfig/node14": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "devOptional": true + "dev": true }, "node_modules/@tsconfig/node16": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", - "devOptional": true + "dev": true }, "node_modules/@turf/along": { "version": "7.2.0", @@ -8047,6 +8028,15 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, + "node_modules/base64-arraybuffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz", + "integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, "node_modules/base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", @@ -9017,7 +9007,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "devOptional": true + "dev": true }, "node_modules/cross-env": { "version": "7.0.3", @@ -9102,6 +9092,15 @@ "postcss": "^8.4" } }, + "node_modules/css-line-break": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-2.1.0.tgz", + "integrity": "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==", + "license": "MIT", + "dependencies": { + "utrie": "^1.0.2" + } + }, "node_modules/css-loader": { "version": "6.11.0", "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.11.0.tgz", @@ -9885,7 +9884,7 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "devOptional": true, + "dev": true, "engines": { "node": ">=0.3.1" } @@ -12489,6 +12488,19 @@ } } }, + "node_modules/html2canvas": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.4.1.tgz", + "integrity": "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==", + "license": "MIT", + "dependencies": { + "css-line-break": "^2.1.0", + "text-segmentation": "^1.0.3" + }, + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/htmlparser2": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", @@ -15235,7 +15247,7 @@ "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "devOptional": true + "dev": true }, "node_modules/makeerror": { "version": "1.0.12", @@ -20434,6 +20446,15 @@ "node": "*" } }, + "node_modules/text-segmentation": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/text-segmentation/-/text-segmentation-1.0.3.tgz", + "integrity": "sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==", + "license": "MIT", + "dependencies": { + "utrie": "^1.0.2" + } + }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -20694,7 +20715,7 @@ "version": "10.9.2", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", - "devOptional": true, + "dev": true, "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -20737,7 +20758,7 @@ "version": "8.3.4", "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", - "devOptional": true, + "dev": true, "dependencies": { "acorn": "^8.11.0" }, @@ -20749,7 +20770,7 @@ "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "devOptional": true + "dev": true }, "node_modules/tsconfig-paths": { "version": "3.15.0", @@ -21220,6 +21241,15 @@ "node": ">= 0.4.0" } }, + "node_modules/utrie": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/utrie/-/utrie-1.0.2.tgz", + "integrity": "sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==", + "license": "MIT", + "dependencies": { + "base64-arraybuffer": "^1.0.2" + } + }, "node_modules/uuid": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", @@ -21236,7 +21266,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "devOptional": true + "dev": true }, "node_modules/v8-to-istanbul": { "version": "8.1.1", @@ -22295,7 +22325,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "devOptional": true, + "dev": true, "engines": { "node": ">=6" } diff --git a/app/package.json b/app/package.json index 66e3b39..ce5c7d3 100644 --- a/app/package.json +++ b/app/package.json @@ -24,6 +24,7 @@ "chartjs-plugin-annotation": "^3.1.0", "glob": "^11.0.0", "gsap": "^3.12.5", + "html2canvas": "^1.4.1", "leva": "^0.10.0", "mqtt": "^5.10.4", "postprocessing": "^6.36.4", diff --git a/app/src/components/icons/RealTimeVisulationIcons.tsx b/app/src/components/icons/RealTimeVisulationIcons.tsx index 1d144d6..84d8cca 100644 --- a/app/src/components/icons/RealTimeVisulationIcons.tsx +++ b/app/src/components/icons/RealTimeVisulationIcons.tsx @@ -8,37 +8,45 @@ export function CleanPannel() { xmlns="http://www.w3.org/2000/svg" > - + - + diff --git a/app/src/components/layout/sidebarLeft/SideBarLeft.tsx b/app/src/components/layout/sidebarLeft/SideBarLeft.tsx index fb7496a..b1aad0b 100644 --- a/app/src/components/layout/sidebarLeft/SideBarLeft.tsx +++ b/app/src/components/layout/sidebarLeft/SideBarLeft.tsx @@ -49,7 +49,7 @@ const SideBarLeft: React.FC = () => { ) : activeModule === "market" ? ( <> - ) : ( + ) : activeModule === "builder" ? ( <> { {activeOption === "Outline" ? : } + ) : ( + <> + +
+ {activeOption === "Outline" ? : } +
+ )} )} diff --git a/app/src/components/layout/sidebarLeft/visualization/widgets/Widgets2D.tsx b/app/src/components/layout/sidebarLeft/visualization/widgets/Widgets2D.tsx index 561c82b..c26dc85 100644 --- a/app/src/components/layout/sidebarLeft/visualization/widgets/Widgets2D.tsx +++ b/app/src/components/layout/sidebarLeft/visualization/widgets/Widgets2D.tsx @@ -14,7 +14,7 @@ const chartTypes: ChartType[] = [ ]; const sampleData = { - labels: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul"], + labels: ["January", "February", "March", "April", "May", "June", "July"], datasets: [ { data: [65, 59, 80, 81, 56, 55, 40], diff --git a/app/src/components/layout/sidebarRight/visualization/design/Design.tsx b/app/src/components/layout/sidebarRight/visualization/design/Design.tsx index f0908b8..e78b4dd 100644 --- a/app/src/components/layout/sidebarRight/visualization/design/Design.tsx +++ b/app/src/components/layout/sidebarRight/visualization/design/Design.tsx @@ -1,18 +1,21 @@ -import { useState } from "react"; +import { useState, useEffect, useRef } from "react"; import { useWidgetStore } from "../../../../../store/useWidgetStore"; import ChartComponent from "../../../sidebarLeft/visualization/widgets/ChartComponent"; import RegularDropDown from "../../../../ui/inputs/RegularDropDown"; +import { WalletIcon } from "../../../../icons/3dChartIcons"; +import SimpleCard from "../../../../ui/realTimeVis/floating/SimpleCard"; -// Define Props Interface interface Widget { id: string; - type: string; // Chart type (e.g., "bar", "line") - panel: "top" | "bottom" | "left" | "right"; // Panel location - title: string; + type?: string; + panel: "top" | "bottom" | "left" | "right"; + title?: string; + header?: string; fontFamily?: string; fontSize?: string; fontWeight?: string; - data: { + className?: string; + data?: { labels: string[]; datasets: { data: number[]; @@ -20,162 +23,263 @@ interface Widget { borderColor: string; borderWidth: number; }[]; - }; // Data for the chart + }; + value?: string; + per?: string; +} + +interface ChartElement { + tagName: string; + className: string; + textContent: string; + selector: string; } const Design = () => { - const [selectedName, setSelectedName] = useState("drop down"); - console.log("selectedName: ", selectedName); - - const [selectedElement, setSelectedElement] = useState("drop down"); - console.log("selectedElement: ", selectedElement); - const [selectedFont, setSelectedFont] = useState("drop down"); - console.log("selectedFont: ", selectedFont); - const [selectedSize, setSelectedSize] = useState("drop down"); - console.log("selectedSize: ", selectedSize); - const [selectedWeight, setSelectedWeight] = useState("drop down"); - console.log("selectedWeight: ", selectedWeight); + const [elementColor, setElementColor] = useState("#6f42c1"); + const [showColorPicker, setShowColorPicker] = useState(false); + const [chartElements, setChartElements] = useState([]); + const [selectedElementToStyle, setSelectedElementToStyle] = useState(null); + const [nameInput, setNameInput] = useState(""); + const chartRef = useRef(null); - const [elementColor, setElementColor] = useState("#6f42c1"); // Default color for elements - const [showColorPicker, setShowColorPicker] = useState(false); // Manage visibility of the color picker + const { selectedChartId, setSelectedChartId, widgets, setWidgets } = useWidgetStore(); - // Zustand Store Hooks - const { selectedChartId, setSelectedChartId, widgets, setWidgets } = - useWidgetStore(); + // Initialize name input and extract elements when selectedChartId changes + useEffect(() => { + setNameInput(selectedChartId?.header || selectedChartId?.title || ""); + + if (!chartRef.current) return; + + const timer = setTimeout(() => { + const chartContainer = chartRef.current; + if (!chartContainer) return; + + const elements = Array.from(chartContainer.querySelectorAll("*")) + .filter((el) => { + const tagName = el.tagName.toLowerCase(); + return !["script", "style", "meta", "link", "head"].includes(tagName); + }) + .map((el, index) => { + const tagName = el.tagName.toLowerCase(); + const className = typeof el.className === "string" ? el.className : ""; + const textContent = el.textContent?.trim() || ""; + + let selector = tagName; + + if (className && typeof className === "string") { + const classList = className.split(/\s+/).filter((c) => c.length > 0); + if (classList.length > 0) { + selector += "." + classList.join("."); + } + } + + if (!className || className.trim() === "") { + const parent = el.parentElement; + if (parent) { + const siblings = Array.from(parent.children).filter( + (child) => child.tagName.toLowerCase() === tagName + ); + const position = siblings.indexOf(el) + 1; + selector += `:nth-of-type(${position})`; + } + } + + return { + tagName, + className, + textContent, + selector, + }; + }); + + setChartElements(elements); + }, 300); + + return () => clearTimeout(timer); + }, [selectedChartId]); + + const applyStyles = () => { + if (!selectedElementToStyle || !chartRef.current) return; + + const element = chartRef.current.querySelector(selectedElementToStyle); + if (!element) return; + + const elementToStyle = element as HTMLElement; + + if (selectedFont !== "drop down") { + elementToStyle.style.fontFamily = selectedFont; + } + if (selectedSize !== "drop down") { + elementToStyle.style.fontSize = selectedSize; + } + if (selectedWeight !== "drop down") { + elementToStyle.style.fontWeight = selectedWeight.toLowerCase(); + } + if (elementColor) { + elementToStyle.style.color = elementColor; + } + }; + + useEffect(() => { + applyStyles(); + }, [selectedFont, selectedSize, selectedWeight, elementColor, selectedElementToStyle]); - // Handle Widget Updates const handleUpdateWidget = (updatedProperties: Partial) => { if (!selectedChartId) return; - // Update the selectedChartId const updatedChartId = { ...selectedChartId, ...updatedProperties, }; setSelectedChartId(updatedChartId); - // Update the widgets array const updatedWidgets = widgets.map((widget) => - widget.id === selectedChartId.id - ? { ...widget, ...updatedProperties } - : widget + widget.id === selectedChartId.id ? { ...widget, ...updatedProperties } : widget ); setWidgets(updatedWidgets); }; - // Default Chart Data + const handleNameChange = (e: React.ChangeEvent) => { + const newName = e.target.value; + setNameInput(newName); + + if (selectedChartId?.title) { + handleUpdateWidget({ title: newName }); + } else if (selectedChartId?.header) { + handleUpdateWidget({ header: newName }); + } + }; + const defaultChartData = { labels: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul"], datasets: [ { data: [65, 59, 80, 81, 56, 55, 40], - backgroundColor: elementColor, // Default background color - borderColor: "#ffffff", // Default border color + backgroundColor: elementColor, + borderColor: "#ffffff", borderWidth: 1, }, ], }; + const elementOptions = chartElements.map((el) => { + let displayName = el.tagName; + if (el.className) displayName += `.${el.className}`; + if (el.textContent) + displayName += ` (${el.textContent.substring(0, 20)}${ + el.textContent.length > 20 ? "..." : "" + })`; + return { + display: displayName, + value: el.selector, + }; + }); + return (
- {/* Title of the Selected Widget */}
- {selectedChartId?.title || "Widget 1"} + {selectedChartId?.title || selectedChartId?.header || "Widget 1"}
- {/* Chart Component */} -
- {selectedChartId && ( +
+ {selectedChartId?.title ? ( + ) : ( + )}
- {/* Options Container */}
- {/* Name Dropdown */} +
+ Element to Style + 0 + ? elementOptions.map((opt) => opt.display) + : ["No elements found"] + } + onSelect={(value) => { + const selected = elementOptions.find( + (opt) => opt.display === value + ); + setSelectedElementToStyle(selected?.value || null); + }} + /> +
+
Name - { - setSelectedName(value); - handleUpdateWidget({ title: value }); - }} +
- {/* Element Dropdown */} -
- Element - { - setSelectedElement(value); - handleUpdateWidget({ type: value }); - }} - /> -
+ {selectedChartId?.title && ( +
+ Chart Type + { + handleUpdateWidget({ type: value }); + }} + /> +
+ )} - {/* Font Family Dropdown */}
Font Family { - setSelectedFont(value); - handleUpdateWidget({ fontFamily: value }); - }} + onSelect={(value) => setSelectedFont(value)} />
- {/* Size Dropdown */}
Size { - setSelectedSize(value); - handleUpdateWidget({ fontSize: value }); - }} + onSelect={(value) => setSelectedSize(value)} />
- {/* Weight Dropdown */}
Weight { - setSelectedWeight(value); - handleUpdateWidget({ fontWeight: value }); - }} + onSelect={(value) => setSelectedWeight(value)} />
- {/* Element Color Picker */}
setShowColorPicker((prev) => !prev)} > Element Color -
{" "} - {/* Change icon based on the visibility */} +
- {/* Show color picker only when 'showColorPicker' is true */} {showColorPicker && (
{ value={elementColor} onChange={(e) => { setElementColor(e.target.value); - handleUpdateWidget({ - data: { - labels: selectedChartId?.data?.labels || [], - datasets: [ - { - ...selectedChartId?.data?.datasets[0], - backgroundColor: e.target.value, // Update the element color - }, - ], - }, - }); + if (selectedChartId?.data) { + handleUpdateWidget({ + data: { + ...selectedChartId.data, + datasets: [ + { + ...selectedChartId.data.datasets[0], + backgroundColor: e.target.value, + }, + ], + }, + }); + } }} /> - {/* Display the selected color value */} {elementColor}
)} @@ -206,4 +311,4 @@ const Design = () => { ); }; -export default Design; +export default Design; \ No newline at end of file diff --git a/app/src/components/ui/componets/Panel.tsx b/app/src/components/ui/componets/Panel.tsx index 7d34c3f..8ec26a0 100644 --- a/app/src/components/ui/componets/Panel.tsx +++ b/app/src/components/ui/componets/Panel.tsx @@ -124,8 +124,8 @@ const Panel: React.FC = ({ selectedZone.widgets.filter((w) => w.panel === panel).length; const calculatePanelCapacity = (panel: Side) => { - const CHART_WIDTH = 150; - const CHART_HEIGHT = 150; + const CHART_WIDTH = 170; + const CHART_HEIGHT = 170; const FALLBACK_HORIZONTAL_CAPACITY = 5; const FALLBACK_VERTICAL_CAPACITY = 3; diff --git a/app/src/components/ui/componets/RealTimeVisulization.tsx b/app/src/components/ui/componets/RealTimeVisulization.tsx index 619cbb0..5cb8867 100644 --- a/app/src/components/ui/componets/RealTimeVisulization.tsx +++ b/app/src/components/ui/componets/RealTimeVisulization.tsx @@ -231,7 +231,7 @@ const RealTimeVisulization: React.FC = () => { onDrop={(event) => handleDrop(event)} onDragOver={(event) => event.preventDefault()} > - + {/* */}
{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 5e3db3f..325ad14 100644 --- a/app/src/components/ui/componets/functions/determinePosition.ts +++ b/app/src/components/ui/componets/functions/determinePosition.ts @@ -1,4 +1,3 @@ - export function determinePosition( canvasRect: DOMRect, relativeX: number, @@ -12,6 +11,23 @@ export function determinePosition( const centerX = canvasRect.width / 2; const centerY = canvasRect.height / 2; + // Define a threshold for considering a point as "centered" + const centerThreshold = 10; // Adjust this value as needed + + // Check if the point is within the center threshold + const isCenterX = Math.abs(relativeX - centerX) <= centerThreshold; + const isCenterY = Math.abs(relativeY - centerY) <= centerThreshold; + + // If the point is centered, return a special "centered" position + if (isCenterX && isCenterY) { + return { + top: "auto", + left: "auto", + right: "auto", + bottom: "auto", + }; + } + let position: { top: number | "auto"; left: number | "auto"; @@ -21,7 +37,7 @@ export function determinePosition( if (relativeY < centerY) { if (relativeX < centerX) { - + // Top-left quadrant position = { top: relativeY - 41.5, left: relativeX - 125, @@ -29,7 +45,7 @@ export function determinePosition( bottom: "auto", }; } else { - + // Top-right quadrant position = { top: relativeY - 41.5, right: canvasRect.width - relativeX - 125, @@ -39,7 +55,7 @@ export function determinePosition( } } else { if (relativeX < centerX) { - + // Bottom-left quadrant position = { bottom: canvasRect.height - relativeY - 41.5, left: relativeX - 125, @@ -47,7 +63,7 @@ export function determinePosition( top: "auto", }; } else { - + // Bottom-right quadrant position = { bottom: canvasRect.height - relativeY - 41.5, right: canvasRect.width - relativeX - 125, @@ -58,4 +74,4 @@ export function determinePosition( } return position; -} +} \ No newline at end of file diff --git a/app/src/components/ui/list/DropDownList.tsx b/app/src/components/ui/list/DropDownList.tsx index e77f35b..ecdf7cf 100644 --- a/app/src/components/ui/list/DropDownList.tsx +++ b/app/src/components/ui/list/DropDownList.tsx @@ -32,14 +32,22 @@ const DropDownList: React.FC = ({ listType = "default", remove, }) => { - const [isOpen, setIsOpen] = useState(defaultOpen); - const { zones, setZones } = useZones() + const { zones, setZones } = useZones(); const handleToggle = () => { setIsOpen((prev) => !prev); // Toggle the state }; - const [zoneDataList, setZoneDataList] = useState<{ id: string; name: string }[]>([]); + + interface Asset { + id: string; + name: string; + } + + const [zoneDataList, setZoneDataList] = useState< + { id: string; name: string; assets: Asset[] }[] + >([]); + const { selectedZone, setSelectedZone } = useSelectedZoneStore(); useEffect(() => { @@ -53,12 +61,64 @@ const DropDownList: React.FC = ({ // { id: "70fa55cd-b5c9-4f80-a8c4-6319af3bfb4e", name: "zone6" }, // ]) - - const value = (zones || []).map((val: { zoneId: string; zoneName: string }) => ({ - id: val.zoneId, - name: val.zoneName - })); - setZoneDataList(prev => (JSON.stringify(prev) !== JSON.stringify(value) ? value : prev)); + const value = (zones || []).map( + (val: { zoneId: string; zoneName: string }) => ({ + id: val.zoneId, + name: val.zoneName, + }) + ); + setZoneDataList([ + { + id: "zone1", + name: "Zone 1", + assets: [ + { + id: "asset1", + name: "Asset 1", + }, + { + id: "asset2", + name: "Asset 2", + }, + { + id: "asset3", + name: "Asset 3", + }, + ], + }, + { + id: "zone2", + name: "Zone 2", + assets: [ + { + id: "asset4", + name: "Asset 4", + }, + { + id: "asset5", + name: "Asset 5", + }, + { + id: "asset6", + name: "Asset 6", + }, + ], + }, + { + id: "zone3", + name: "Zone 3", + assets: [ + { + id: "asset7", + name: "Asset 7", + }, + { + id: "asset8", + name: "Asset 8", + }, + ], + }, + ]); }, [zones]); return ( @@ -101,6 +161,7 @@ const DropDownList: React.FC = ({ value="Buildings" showKebabMenu={false} showAddIcon={false} + items={zoneDataList} /> = ({ items = [], remove }) => { + console.log("items: ", items); const { activeModule, setActiveModule } = useModuleStore(); const { selectedZone, setSelectedZone } = useSelectedZoneStore(); const { setSubModule } = useSubModuleStore(); + const [expandedZones, setExpandedZones] = useState>( + {} + ); + useEffect(() => { useSelectedZoneStore.getState().setSelectedZone({ zoneName: "", @@ -27,11 +51,16 @@ const List: React.FC = ({ items = [], remove }) => { widgets: [], }); }, [activeModule]); - + + const toggleZoneExpansion = (zoneId: string) => { + setExpandedZones((prev) => ({ + ...prev, + [zoneId]: !prev[zoneId], + })); + }; async function handleSelectZone(id: string) { try { - // Avoid re-fetching if the same zone is already selected if (selectedZone?.zoneId === id) { console.log("Zone is already selected:", selectedZone.zoneName); return; @@ -65,27 +94,74 @@ const List: React.FC = ({ items = [], remove }) => { <> {items.length > 0 ? (
    - {items.map((item, index) => ( -
  • -
    -
    handleSelectZone(item.id)}> - -
    -
    -
    - -
    -
    - -
    - {remove && ( -
    - + {items.map((item) => ( + +
  • +
    +
    +
    handleSelectZone(item.id)} + > +
    - )} +
    +
    +
    + +
    +
    + +
    + {remove && ( +
    + +
    + )} + {item.assets && item.assets.length > 0 && ( +
    toggleZoneExpansion(item.id)} + > + +
    + )} +
    -
- + + {/* Nested assets list - only shown when expanded */} + {item.assets && + item.assets.length > 0 && + expandedZones[item.id] && ( +
    + {item.assets.map((asset) => ( +
  • +
    +
    + +
    +
    +
    + +
    +
    + +
    + {remove && ( +
    + +
    + )} +
    +
    +
  • + ))} +
+ )} + ))} ) : ( diff --git a/app/src/modules/visualization/handleSaveTemplate.ts b/app/src/modules/visualization/handleSaveTemplate.ts index 6a3bad4..e5f90ab 100644 --- a/app/src/modules/visualization/handleSaveTemplate.ts +++ b/app/src/modules/visualization/handleSaveTemplate.ts @@ -30,7 +30,7 @@ export const handleSaveTemplate = async ({ }: HandleSaveTemplateProps): Promise => { try { // Check if the selected zone has any widgets - if (!selectedZone.widgets || selectedZone.widgets.length === 0) { + if (!selectedZone.panelOrder || selectedZone.panelOrder.length === 0) { console.warn("No widgets found in the selected zone."); return; } diff --git a/app/src/pages/Project.tsx b/app/src/pages/Project.tsx index 54fe61f..88fd467 100644 --- a/app/src/pages/Project.tsx +++ b/app/src/pages/Project.tsx @@ -56,7 +56,7 @@ const Project: React.FC = () => { return (
- {loadingProgress && } + {/* {loadingProgress && } */} {!isPlaying && ( <> {toggleThreeD && } diff --git a/app/src/services/realTimeVisulization/zoneData/saveTempleteApi.ts b/app/src/services/realTimeVisulization/zoneData/saveTempleteApi.ts index 5c18031..9eae5cb 100644 --- a/app/src/services/realTimeVisulization/zoneData/saveTempleteApi.ts +++ b/app/src/services/realTimeVisulization/zoneData/saveTempleteApi.ts @@ -1,7 +1,7 @@ let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`; // let url_Backend_dwinzo = `http://192.168.0.102:5000`; export const saveTemplateApi = async (organization: string, template: {}) => { - console.log('template: ', template); + console.log('template: ', template); try { const response = await fetch(`${url_Backend_dwinzo}/api/v2/template/save`, { method: "POST", @@ -16,7 +16,7 @@ export const saveTemplateApi = async (organization: string, template: {}) => { } const result = await response.json(); - console.log('result: ', result); + return result; } catch (error) { if (error instanceof Error) { diff --git a/app/src/styles/components/lists.scss b/app/src/styles/components/lists.scss index a9b0145..f396a50 100644 --- a/app/src/styles/components/lists.scss +++ b/app/src/styles/components/lists.scss @@ -3,15 +3,23 @@ .dropdown-list-container { border-bottom: 1px solid var(--border-color); + + .lists-container { + margin-bottom: 6px; + } + &:last-child { border: none; } + .head { @include flex-space-between; padding: 6px 12px; + .options { @include flex-center; gap: 6px; + .option { @include flex-center; cursor: pointer; @@ -22,34 +30,52 @@ } .list-wrapper { + + .no-item { padding: 12px; } + .list-container { padding: 2px; + margin-left: 10px; + overflow: hidden; + + .list-item { @include flex-space-between; width: 100%; text-align: center; - padding: 4px 12px; + padding: 4px 8px; border-radius: #{$border-radius-large}; + .value { width: 100%; text-align: start; max-width: 180px; } + .options-container { @include flex-center; gap: 6px; + .option { @include flex-center; cursor: pointer; } } } + .active { background-color: var(--highlight-accent-color); color: var(--primary-color); } } -} + + .asset-list { + border-left: 2px solid var(--border-color); + + margin-left: 20px + } + +} \ No newline at end of file diff --git a/app/src/styles/layout/sidebar.scss b/app/src/styles/layout/sidebar.scss index 63297c9..d4cf595 100644 --- a/app/src/styles/layout/sidebar.scss +++ b/app/src/styles/layout/sidebar.scss @@ -253,6 +253,7 @@ .user-profile-container { display: flex; + .user-profile { background: var(--accent-color); color: var(--primary-color); @@ -483,6 +484,9 @@ height: 150px; background: #f0f0f0; // border-radius: 8px; + display: flex; + align-items: center; + // justify-content: center; } .optionsContainer { @@ -497,7 +501,8 @@ justify-content: space-between; gap: 12px; - .regularDropdown-container { + .regularDropdown-container, + input { width: 160px; } @@ -686,6 +691,7 @@ font-weight: var(--font-weight-regular); padding: 8px 0; } + .input-toggle-container { padding: 0; margin-bottom: 6px; @@ -1009,11 +1015,9 @@ top: 50%; right: -10px; transform: translate(0, -50%); - background: linear-gradient( - 144.19deg, - #f1e7cd 16.62%, - #fffaef 85.81% - ); + background: linear-gradient(144.19deg, + #f1e7cd 16.62%, + #fffaef 85.81%); } .category-image { @@ -1117,11 +1121,13 @@ } } } + .assets-result { width: 100%; height: 100%; margin: 8px 10px; + .assets-wrapper { margin: 0; } -} +} \ No newline at end of file diff --git a/app/src/styles/pages/realTimeViz.scss b/app/src/styles/pages/realTimeViz.scss index e64ec08..fd42be0 100644 --- a/app/src/styles/pages/realTimeViz.scss +++ b/app/src/styles/pages/realTimeViz.scss @@ -61,7 +61,7 @@ border-radius: 8px; max-width: 80%; overflow: auto; - // max-width: calc(100% - 450px); + max-width: calc(100% - 500px); &::-webkit-scrollbar { display: none; @@ -170,10 +170,12 @@ box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); transition: all 0.3s ease; border-radius: 6px; - overflow: visible !important; + overflow: auto; z-index: $z-index-tools; overflow: auto; - + &::-webkit-scrollbar { + display: none; + } .panel-content { position: relative; height: 100%; @@ -319,6 +321,7 @@ right: 0; top: 0; bottom: 0; + } } @@ -715,6 +718,13 @@ z-index: 2 !important; } +.connectionSuccess { + outline-color: #43C06D; +} + +.connectionFails { + outline-color: #ffe3e0; +} .editWidgetOptions-wrapper { From 60b778ff3d60a34a4659c220a47665d6dacf84d9 Mon Sep 17 00:00:00 2001 From: Gomathi9520 Date: Fri, 4 Apr 2025 15:17:38 +0530 Subject: [PATCH 2/4] rotation added --- .../3D-cards/cards/ProductionCapacity.tsx | 39 ++- .../ui/componets/DraggableWidget.tsx | 3 +- .../ui/componets/Dropped3dWidget.tsx | 234 ++++++++++++------ .../ui/componets/RealTimeVisulization.tsx | 5 +- .../components/ui/menu/EditWidgetOption.tsx | 10 +- app/src/store/useZone3DWidgetStore.ts | 39 ++- 6 files changed, 239 insertions(+), 91 deletions(-) diff --git a/app/src/components/layout/3D-cards/cards/ProductionCapacity.tsx b/app/src/components/layout/3D-cards/cards/ProductionCapacity.tsx index 4ec9fdd..0d239b4 100644 --- a/app/src/components/layout/3D-cards/cards/ProductionCapacity.tsx +++ b/app/src/components/layout/3D-cards/cards/ProductionCapacity.tsx @@ -30,14 +30,13 @@ interface ProductionCapacityProps { id: string; type: string; position: [number, number, number]; + rotation: [number, number, number]; onContextMenu?: (event: React.MouseEvent) => void; // onPointerDown:any } -const ProductionCapacity: React.FC = ({ id, type, position, onContextMenu }) => { - +const ProductionCapacity: React.FC = ({ id, type, position, rotation, onContextMenu }) => { const { selectedChartId, setSelectedChartId } = useWidgetStore(); - const { measurements: chartMeasurements, duration: chartDuration, name: widgetName } = useChartStore(); const [measurements, setmeasurements] = useState({}); const [duration, setDuration] = useState("1h") @@ -177,15 +176,43 @@ const ProductionCapacity: React.FC = ({ id, type, posit } , [chartMeasurements, chartDuration, widgetName]) + useEffect(() => { + + console.log('rotation: ', rotation); + }, [rotation]) + const rotationDegrees = { + x: (rotation[0] * 180) / Math.PI, + y: (rotation[1] * 180) / Math.PI, + z: (rotation[2] * 180) / Math.PI, + }; + + const transformStyle = { + transform: `rotateX(${rotationDegrees.x}deg) rotateY(${rotationDegrees.y}deg) rotateZ(${rotationDegrees.z}deg)`, + }; + return ( - + sprite + zIndexRange={[1,0]} + // center + // distanceFactor={10} // Adjusted for visual balance + style={{ + transform: transformStyle.transform, + transformStyle: 'preserve-3d', + transition: 'transform 0.1s ease-out' + + }}>
setSelectedChartId({ id: id, type: type })} onContextMenu={onContextMenu} + style={{ + width: '300px', // Original width + height: '300px', // Original height + transform: transformStyle.transform, + transformStyle: 'preserve-3d' + }} >
Production Capacity
diff --git a/app/src/components/ui/componets/DraggableWidget.tsx b/app/src/components/ui/componets/DraggableWidget.tsx index 41727db..fa7cff8 100644 --- a/app/src/components/ui/componets/DraggableWidget.tsx +++ b/app/src/components/ui/componets/DraggableWidget.tsx @@ -239,14 +239,13 @@ export const DraggableWidget = ({ onReorder(fromIndex, toIndex); // Call the reorder function passed as a prop } }; - console.log("widget.type", widget.type); // useClickOutside(chartWidget, () => { // setSelectedChartId(null); // }); const { isPlaying } = usePlayButtonStore(); - console.log('isPanelHidden: ', isPanelHidden); + return ( <>
([0, 0, 0]); + const mouseStartRef = useRef<{ x: number; y: number }>({ x: 0, y: 0 }); useEffect(() => { if (activeModule !== "visualization") return; @@ -48,14 +56,14 @@ export default function Dropped3dWidgets() { const organization = email?.split("@")[1]?.split(".")[0]; async function get3dWidgetData() { - let result = await get3dWidgetZoneData(selectedZone.zoneId, organization); - + const result = await get3dWidgetZoneData(selectedZone.zoneId, organization); setWidgets3D(result); - const formattedWidgets = result.map((widget: any) => ({ + const formattedWidgets = result.map((widget: WidgetData) => ({ id: widget.id, type: widget.type, position: widget.position, + rotation: widget.rotation || [0, 0, 0], })); setZoneWidgetData(selectedZone.zoneId, formattedWidgets); @@ -91,28 +99,24 @@ export default function Dropped3dWidgets() { if (intersects.length > 0) { const { x, y, z } = intersects[0].point; - const newWidget = { + const newWidget: WidgetData = { id: generateUniqueId(), type: widgetSelect, - position: [x, y, z] as [number, number, number], + position: [x, y, z], + rotation: [0, 0, 0], }; - let add3dWidget = { + const add3dWidget = { organization: organization, widget: newWidget, zoneId: selectedZone.zoneId - } + }; if (visualizationSocket) { - visualizationSocket.emit("v2:viz-3D-widget:add", add3dWidget) + visualizationSocket.emit("v2:viz-3D-widget:add", add3dWidget); } - // let response = await adding3dWidgets(selectedZone.zoneId, organization, newWidget); - // - - // if (response.message === "Widget created successfully") { addWidget(selectedZone.zoneId, newWidget); - // } } }; @@ -128,63 +132,76 @@ export default function Dropped3dWidgets() { if (!rightClickSelected) return; const email = localStorage.getItem("email") || ""; const organization = email?.split("@")[1]?.split(".")[0]; + if (rightSelect === "Duplicate") { - const widgetToDuplicate = activeZoneWidgets.find(w => w.id === rightClickSelected); + const widgetToDuplicate = activeZoneWidgets.find((w: WidgetData) => w.id === rightClickSelected); if (!widgetToDuplicate) return; - const newWidget = { + const newWidget: WidgetData = { id: generateUniqueId(), type: widgetToDuplicate.type, position: [ - widgetToDuplicate.position[0] + 0.5, // Slightly shift position + widgetToDuplicate.position[0] + 0.5, widgetToDuplicate.position[1], widgetToDuplicate.position[2] + 0.5, - ] as [number, number, number], + ], + rotation: widgetToDuplicate.rotation || [0, 0, 0], }; - let add3dWidget = { + + const add3dWidget = { organization, widget: newWidget, zoneId: selectedZone.zoneId }; - // if (visualizationSocket) { - // visualizationSocket.emit("v2:viz-3D-widget:add", add3dWidget); - // } + addWidget(selectedZone.zoneId, newWidget); setRightSelect(null); setRightClickSelected(null); } + if (rightSelect === "Delete") { - let deleteWidget = { + const deleteWidget = { organization, widgetId: rightClickSelected, zoneId: selectedZone.zoneId }; - // if (visualizationSocket) { - // visualizationSocket.emit("v2:viz-3D-widget:delete", deleteWidget); - // } - setZoneWidgetData(selectedZone.zoneId, activeZoneWidgets.filter(w => w.id !== rightClickSelected)); + + setZoneWidgetData( + selectedZone.zoneId, + activeZoneWidgets.filter((w: WidgetData) => w.id !== rightClickSelected) + ); setRightClickSelected(null); setRightSelect(null); } - if (rightSelect === "Horizontal Move") { - - } - if (rightSelect === "Vertical Move") { - - } - }, [rightSelect, rightClickSelected]); useEffect(() => { - const handleMouseMove = (event: MouseEvent) => { + const handleMouseDown = (event: MouseEvent) => { if (!rightClickSelected || !rightSelect) return; + if (rightSelect === "RotateX" || rightSelect === "RotateY") { + mouseStartRef.current = { x: event.clientX, y: event.clientY }; - const selectedZone = Object.keys(zoneWidgetData).find(zoneId => - zoneWidgetData[zoneId].some(widget => widget.id === rightClickSelected) + const selectedZone = Object.keys(zoneWidgetData).find((zoneId: string) => + zoneWidgetData[zoneId].some((widget: WidgetData) => widget.id === rightClickSelected) + ); + + if (!selectedZone) return; + + const selectedWidget = zoneWidgetData[selectedZone].find((widget: WidgetData) => widget.id === rightClickSelected); + if (selectedWidget) { + rotationStartRef.current = selectedWidget.rotation || [0, 0, 0]; + } + } + }; + + const handleMouseMove = (event: MouseEvent) => { + if (!rightClickSelected || !rightSelect) return; + const selectedZone = Object.keys(zoneWidgetData).find((zoneId: string) => + zoneWidgetData[zoneId].some((widget: WidgetData) => widget.id === rightClickSelected) ); if (!selectedZone) return; - const selectedWidget = zoneWidgetData[selectedZone].find(widget => widget.id === rightClickSelected); + const selectedWidget = zoneWidgetData[selectedZone].find((widget: WidgetData) => widget.id === rightClickSelected); if (!selectedWidget) return; const rect = gl.domElement.getBoundingClientRect(); @@ -194,31 +211,67 @@ export default function Dropped3dWidgets() { raycaster.setFromCamera(mouse, camera); if (rightSelect === "Horizontal Move" && raycaster.ray.intersectPlane(plane.current, planeIntersect.current)) { - - updateWidgetPosition(selectedZone, rightClickSelected, [ + const newPosition: [number, number, number] = [ planeIntersect.current.x, selectedWidget.position[1], planeIntersect.current.z - ]); + ]; + updateWidgetPosition(selectedZone, rightClickSelected, newPosition); + console.log('Horizontal Move - Final Position:', newPosition); } if (rightSelect === "Vertical Move") { if (raycaster.ray.intersectPlane(verticalPlane.current, planeIntersect.current)) { updateWidgetPosition(selectedZone, rightClickSelected, [ selectedWidget.position[0], - planeIntersect.current.y, // Ensure Y value updates correctly + planeIntersect.current.y, selectedWidget.position[2] ]); - } else { - console.log("No Intersection with Vertical Plane"); } } + + if (rightSelect === "RotateX") { + const deltaX = event.clientX - mouseStartRef.current.x; + const rotationSpeed = 0.03; + const newRotation: [number, number, number] = [ + rotationStartRef.current[0] + deltaX * rotationSpeed, + rotationStartRef.current[1], + rotationStartRef.current[2] + ]; + updateWidgetRotation(selectedZone, rightClickSelected, newRotation); + } + + if (rightSelect === "RotateY") { + const deltaY = event.clientY - mouseStartRef.current.y; + const rotationSpeed = 0.03; + const newRotation: [number, number, number] = [ + rotationStartRef.current[0], + rotationStartRef.current[1] + deltaY * rotationSpeed, + rotationStartRef.current[2] + ]; + updateWidgetRotation(selectedZone, rightClickSelected, newRotation); + } + if (rightSelect === "RotateZ") { + const deltaX = event.movementX; + const rotationSpeed = 0.03; + const currentRotation = selectedWidget.rotation || [0, 0, 0]; + const newRotation: [number, number, number] = [ + currentRotation[0], + currentRotation[1], + currentRotation[2] + deltaX * rotationSpeed + ]; + updateWidgetRotation(selectedZone, rightClickSelected, newRotation); + } + }; const handleMouseUp = () => { - - if (rightClickSelected && (rightSelect === "Horizontal Move" || rightSelect === "Vertical Move")) { - + if (rightClickSelected && ( + rightSelect === "Horizontal Move" || + rightSelect === "Vertical Move" || + rightSelect === "RotateX" || + rightSelect === "RotateY" || rightSelect === "RotateZ" + )) { setTimeout(() => { setRightClickSelected(null); setRightSelect(null); @@ -226,40 +279,81 @@ export default function Dropped3dWidgets() { } }; - // Attach events to window instead of gl.domElement + window.addEventListener("mousedown", handleMouseDown); window.addEventListener("mousemove", handleMouseMove); window.addEventListener("mouseup", handleMouseUp); return () => { + window.removeEventListener("mousedown", handleMouseDown); window.removeEventListener("mousemove", handleMouseMove); window.removeEventListener("mouseup", handleMouseUp); }; }, [rightClickSelected, rightSelect, zoneWidgetData, gl]); - - - return ( <> - {activeZoneWidgets.map(({ id, type, position }) => { - const handleRightClick = (event: React.MouseEvent) => { + {activeZoneWidgets.map(({ id, type, position, rotation = [0, 0, 0] }: WidgetData) => { + const handleRightClick = (event: React.MouseEvent, id: string) => { event.preventDefault(); - setRightClickSelected(id) + const canvasElement = document.getElementById("real-time-vis-canvas"); + if (!canvasElement) throw new Error("Canvas element not found"); + const canvasRect = canvasElement.getBoundingClientRect(); + const relativeX = event.clientX - canvasRect.left; + const relativeY = event.clientY - canvasRect.top; + setRightClickSelected(id); + setTop(relativeY); + setLeft(relativeX); }; + switch (type) { case "ui-Widget 1": - return ; + return ( + handleRightClick(e, id)} + /> + ); case "ui-Widget 2": - return ; + return ( + handleRightClick(e, id)} + /> + ); case "ui-Widget 3": - return ; + return ( + handleRightClick(e, id)} + /> + ); case "ui-Widget 4": - return ; + return ( + handleRightClick(e, id)} + /> + ); default: return null; } })} - ); -} +} \ No newline at end of file diff --git a/app/src/components/ui/componets/RealTimeVisulization.tsx b/app/src/components/ui/componets/RealTimeVisulization.tsx index 915e3b3..acbbf68 100644 --- a/app/src/components/ui/componets/RealTimeVisulization.tsx +++ b/app/src/components/ui/componets/RealTimeVisulization.tsx @@ -197,7 +197,6 @@ const RealTimeVisulization: React.FC = () => { } catch (error) { } }; - function handleRightClickSel(){} return (
{ {activeModule === "visualization" && selectedZone.zoneName !== "" && } {activeModule === "visualization" && } - {activeModule === "visualization" && widgetSubOption === "3D" && rightClickSelected && } {activeModule === "visualization" && ( diff --git a/app/src/components/ui/menu/EditWidgetOption.tsx b/app/src/components/ui/menu/EditWidgetOption.tsx index ce5261a..3978430 100644 --- a/app/src/components/ui/menu/EditWidgetOption.tsx +++ b/app/src/components/ui/menu/EditWidgetOption.tsx @@ -17,7 +17,15 @@ const EditWidgetOption: React.FC = ({ options }) => { }, [top, left]) return ( -
+
{options.map((option, index) => (
setRightSelect(option)}> diff --git a/app/src/store/useZone3DWidgetStore.ts b/app/src/store/useZone3DWidgetStore.ts index 850623f..9700878 100644 --- a/app/src/store/useZone3DWidgetStore.ts +++ b/app/src/store/useZone3DWidgetStore.ts @@ -1,10 +1,10 @@ - import { create } from "zustand"; type WidgetData = { id: string; type: string; position: [number, number, number]; + rotation?: [number, number, number]; tempPosition?: [number, number, number]; }; @@ -13,38 +13,59 @@ type ZoneWidgetStore = { setZoneWidgetData: (zoneId: string, widgets: WidgetData[]) => void; addWidget: (zoneId: string, widget: WidgetData) => void; updateWidgetPosition: (zoneId: string, widgetId: string, newPosition: [number, number, number]) => void; + updateWidgetRotation: (zoneId: string, widgetId: string, newRotation: [number, number, number]) => void; }; export const useZoneWidgetStore = create((set) => ({ zoneWidgetData: {}, - setZoneWidgetData: (zoneId, widgets) => - set((state) => ({ + setZoneWidgetData: (zoneId: string, widgets: WidgetData[]) => + set((state: ZoneWidgetStore) => ({ zoneWidgetData: { ...state.zoneWidgetData, [zoneId]: widgets }, })), - addWidget: (zoneId, widget) => - set((state) => ({ + addWidget: (zoneId: string, widget: WidgetData) => + set((state: ZoneWidgetStore) => ({ zoneWidgetData: { ...state.zoneWidgetData, - [zoneId]: [...(state.zoneWidgetData[zoneId] || []), widget], + [zoneId]: [...(state.zoneWidgetData[zoneId] || []), { ...widget, rotation: widget.rotation || [0, 0, 0] }], }, })), - updateWidgetPosition: (zoneId, widgetId, newPosition) => - set((state) => { + updateWidgetPosition: (zoneId: string, widgetId: string, newPosition: [number, number, number]) => + set((state: ZoneWidgetStore) => { const widgets = state.zoneWidgetData[zoneId] || []; return { zoneWidgetData: { ...state.zoneWidgetData, - [zoneId]: widgets.map((widget) => + [zoneId]: widgets.map((widget: WidgetData) => widget.id === widgetId ? { ...widget, position: newPosition } : widget ), }, }; }), + + updateWidgetRotation: (zoneId: string, widgetId: string, newRotation: [number, number, number]) => + set((state: ZoneWidgetStore) => { + const widgets = state.zoneWidgetData[zoneId] || []; + return { + zoneWidgetData: { + ...state.zoneWidgetData, + [zoneId]: widgets.map((widget: WidgetData) => + widget.id === widgetId ? { ...widget, rotation: newRotation } : widget + ), + }, + }; + }), })); +// export type WidgetData = { +// id: string; +// type: string; +// position: [number, number, number]; +// rotation?: [number, number, number]; +// tempPosition?: [number, number, number]; +// }; interface RightClickStore { rightClickSelected: string | null; From 46d4308f9ae20270e7002b7d5c5e05028c6f24e5 Mon Sep 17 00:00:00 2001 From: Nalvazhuthi Date: Fri, 4 Apr 2025 17:47:15 +0530 Subject: [PATCH 3/4] display menu bugfix --- .../ui/componets/Dropped3dWidget.tsx | 694 ++++++++++-------- .../ui/componets/RealTimeVisulization.tsx | 74 +- .../components/ui/menu/EditWidgetOption.tsx | 41 +- app/src/store/useZone3DWidgetStore.ts | 11 + app/src/styles/pages/realTimeViz.scss | 7 +- 5 files changed, 472 insertions(+), 355 deletions(-) diff --git a/app/src/components/ui/componets/Dropped3dWidget.tsx b/app/src/components/ui/componets/Dropped3dWidget.tsx index f12712d..7885275 100644 --- a/app/src/components/ui/componets/Dropped3dWidget.tsx +++ b/app/src/components/ui/componets/Dropped3dWidget.tsx @@ -1,6 +1,10 @@ import { useThree } from "@react-three/fiber"; import React, { useEffect, useRef } from "react"; -import { useAsset3dWidget, useSocketStore, useWidgetSubOption } from "../../../store/store"; +import { + useAsset3dWidget, + useSocketStore, + useWidgetSubOption, +} from "../../../store/store"; import useModuleStore from "../../../store/useModuleStore"; import { ThreeState } from "../../../types/world/worldTypes"; import * as THREE from "three"; @@ -13,347 +17,391 @@ import { generateUniqueId } from "../../../functions/generateUniqueId"; import { adding3dWidgets } from "../../../services/realTimeVisulization/zoneData/add3dWidget"; import { get3dWidgetZoneData } from "../../../services/realTimeVisulization/zoneData/get3dWidgetData"; import { use3DWidget } from "../../../store/useDroppedObjectsStore"; -import { useLeftData, useRightClickSelected, useRightSelected, useTopData, useZoneWidgetStore } from "../../../store/useZone3DWidgetStore"; +import { + useEditWidgetOptionsStore, + useLeftData, + useRightClickSelected, + useRightSelected, + useTopData, + useZoneWidgetStore, +} from "../../../store/useZone3DWidgetStore"; import { useWidgetStore } from "../../../store/useWidgetStore"; import EditWidgetOption from "../menu/EditWidgetOption"; type WidgetData = { - id: string; - type: string; - position: [number, number, number]; - rotation?: [number, number, number]; - tempPosition?: [number, number, number]; + id: string; + type: string; + position: [number, number, number]; + rotation?: [number, number, number]; + tempPosition?: [number, number, number]; }; - export default function Dropped3dWidgets() { - const { widgetSelect } = useAsset3dWidget(); - const { activeModule } = useModuleStore(); - const { raycaster, gl, scene, mouse, camera }: ThreeState = useThree(); - const { widgetSubOption } = useWidgetSubOption(); - const { selectedZone } = useSelectedZoneStore(); - const { top, setTop } = useTopData(); - const { left, setLeft } = useLeftData(); - const { rightSelect, setRightSelect } = useRightSelected(); + const { widgetSelect } = useAsset3dWidget(); + const { activeModule } = useModuleStore(); + const { raycaster, gl, scene, mouse, camera }: ThreeState = useThree(); + const { widgetSubOption } = useWidgetSubOption(); + const { selectedZone } = useSelectedZoneStore(); + const { top, setTop } = useTopData(); + const { left, setLeft } = useLeftData(); + const { rightSelect, setRightSelect } = useRightSelected(); + const { setEditWidgetOptions } = useEditWidgetOptionsStore(); - const { zoneWidgetData, setZoneWidgetData, addWidget, updateWidgetPosition, updateWidgetRotation } = useZoneWidgetStore(); - const { setWidgets3D } = use3DWidget(); - const { visualizationSocket } = useSocketStore(); - const { rightClickSelected, setRightClickSelected } = useRightClickSelected(); - const plane = useRef(new THREE.Plane(new THREE.Vector3(0, 1, 0), 0)); // Floor plane for horizontal move - const verticalPlane = useRef(new THREE.Plane(new THREE.Vector3(0, 0, 1), 0)); // Vertical plane for vertical move - const planeIntersect = useRef(new THREE.Vector3()); - // const plane = useRef(new THREE.Plane(new THREE.Vector3(0, 1, 0), 0); - // const verticalPlane = useRef(new THREE.Plane(new THREE.Vector3(0, 0, 1), 0); - // const planeIntersect = useRef(new THREE.Vector3()); - const rotationStartRef = useRef<[number, number, number]>([0, 0, 0]); - const mouseStartRef = useRef<{ x: number; y: number }>({ x: 0, y: 0 }); + const { + zoneWidgetData, + setZoneWidgetData, + addWidget, + updateWidgetPosition, + updateWidgetRotation, + } = useZoneWidgetStore(); + const { setWidgets3D } = use3DWidget(); + const { visualizationSocket } = useSocketStore(); + const { rightClickSelected, setRightClickSelected } = useRightClickSelected(); + const plane = useRef(new THREE.Plane(new THREE.Vector3(0, 1, 0), 0)); // Floor plane for horizontal move + const verticalPlane = useRef(new THREE.Plane(new THREE.Vector3(0, 0, 1), 0)); // Vertical plane for vertical move + const planeIntersect = useRef(new THREE.Vector3()); + // const plane = useRef(new THREE.Plane(new THREE.Vector3(0, 1, 0), 0); + // const verticalPlane = useRef(new THREE.Plane(new THREE.Vector3(0, 0, 1), 0); + // const planeIntersect = useRef(new THREE.Vector3()); + const rotationStartRef = useRef<[number, number, number]>([0, 0, 0]); + const mouseStartRef = useRef<{ x: number; y: number }>({ x: 0, y: 0 }); - useEffect(() => { - if (activeModule !== "visualization") return; - if (!selectedZone.zoneId) return; + useEffect(() => { + if (activeModule !== "visualization") return; + if (!selectedZone.zoneId) 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() { - const result = await get3dWidgetZoneData(selectedZone.zoneId, organization); - setWidgets3D(result); + async function get3dWidgetData() { + const result = await get3dWidgetZoneData( + selectedZone.zoneId, + organization + ); + setWidgets3D(result); - const formattedWidgets = result.map((widget: WidgetData) => ({ - id: widget.id, - type: widget.type, - position: widget.position, - rotation: widget.rotation || [0, 0, 0], - })); + const formattedWidgets = result.map((widget: WidgetData) => ({ + id: widget.id, + type: widget.type, + position: widget.position, + rotation: widget.rotation || [0, 0, 0], + })); - setZoneWidgetData(selectedZone.zoneId, formattedWidgets); + setZoneWidgetData(selectedZone.zoneId, formattedWidgets); + } + + get3dWidgetData(); + }, [selectedZone.zoneId, activeModule]); + + useEffect(() => { + if (activeModule !== "visualization") return; + if (widgetSubOption === "Floating" || widgetSubOption === "2D") return; + if (selectedZone.zoneName === "") return; + + const canvasElement = gl.domElement; + + const onDrop = async (event: DragEvent) => { + event.preventDefault(); + + 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; + const newWidget: WidgetData = { + id: generateUniqueId(), + type: widgetSelect, + position: [x, y, z], + rotation: [0, 0, 0], + }; + + const add3dWidget = { + organization: organization, + widget: newWidget, + zoneId: selectedZone.zoneId, + }; + + if (visualizationSocket) { + visualizationSocket.emit("v2:viz-3D-widget:add", add3dWidget); } - get3dWidgetData(); - }, [selectedZone.zoneId, activeModule]); + addWidget(selectedZone.zoneId, newWidget); + } + }; - useEffect(() => { - if (activeModule !== "visualization") return; - if (widgetSubOption === "Floating" || widgetSubOption === "2D") return; - if (selectedZone.zoneName === "") return; + canvasElement.addEventListener("drop", onDrop); + return () => { + canvasElement.removeEventListener("drop", onDrop); + }; + }, [widgetSelect, activeModule, selectedZone.zoneId, widgetSubOption]); - const canvasElement = gl.domElement; + const activeZoneWidgets = zoneWidgetData[selectedZone.zoneId] || []; - const onDrop = async (event: DragEvent) => { + useEffect(() => { + if (!rightClickSelected) return; + const email = localStorage.getItem("email") || ""; + const organization = email?.split("@")[1]?.split(".")[0]; + + if (rightSelect === "Duplicate") { + const widgetToDuplicate = activeZoneWidgets.find( + (w: WidgetData) => w.id === rightClickSelected + ); + if (!widgetToDuplicate) return; + const newWidget: WidgetData = { + id: generateUniqueId(), + type: widgetToDuplicate.type, + position: [ + widgetToDuplicate.position[0] + 0.5, + widgetToDuplicate.position[1], + widgetToDuplicate.position[2] + 0.5, + ], + rotation: widgetToDuplicate.rotation || [0, 0, 0], + }; + + const add3dWidget = { + organization, + widget: newWidget, + zoneId: selectedZone.zoneId, + }; + + addWidget(selectedZone.zoneId, newWidget); + setRightSelect(null); + setRightClickSelected(null); + } + + if (rightSelect === "Delete") { + const deleteWidget = { + organization, + widgetId: rightClickSelected, + zoneId: selectedZone.zoneId, + }; + + setZoneWidgetData( + selectedZone.zoneId, + activeZoneWidgets.filter((w: WidgetData) => w.id !== rightClickSelected) + ); + setRightClickSelected(null); + setRightSelect(null); + } + }, [rightSelect, rightClickSelected]); + + useEffect(() => { + const handleMouseDown = (event: MouseEvent) => { + if (!rightClickSelected || !rightSelect) return; + + if (rightSelect === "RotateX" || rightSelect === "RotateY") { + mouseStartRef.current = { x: event.clientX, y: event.clientY }; + + const selectedZone = Object.keys(zoneWidgetData).find( + (zoneId: string) => + zoneWidgetData[zoneId].some( + (widget: WidgetData) => widget.id === rightClickSelected + ) + ); + + if (!selectedZone) return; + + const selectedWidget = zoneWidgetData[selectedZone].find( + (widget: WidgetData) => widget.id === rightClickSelected + ); + if (selectedWidget) { + rotationStartRef.current = selectedWidget.rotation || [0, 0, 0]; + } + } + }; + + const handleMouseMove = (event: MouseEvent) => { + if (!rightClickSelected || !rightSelect) return; + const selectedZone = Object.keys(zoneWidgetData).find((zoneId: string) => + zoneWidgetData[zoneId].some( + (widget: WidgetData) => widget.id === rightClickSelected + ) + ); + if (!selectedZone) return; + + const selectedWidget = zoneWidgetData[selectedZone].find( + (widget: WidgetData) => widget.id === rightClickSelected + ); + if (!selectedWidget) return; + + const rect = gl.domElement.getBoundingClientRect(); + mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1; + mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1; + + raycaster.setFromCamera(mouse, camera); + + if ( + rightSelect === "Horizontal Move" && + raycaster.ray.intersectPlane(plane.current, planeIntersect.current) + ) { + const newPosition: [number, number, number] = [ + planeIntersect.current.x, + selectedWidget.position[1], + planeIntersect.current.z, + ]; + updateWidgetPosition(selectedZone, rightClickSelected, newPosition); + console.log("Horizontal Move - Final Position:", newPosition); + } + + if (rightSelect === "Vertical Move") { + if ( + raycaster.ray.intersectPlane( + verticalPlane.current, + planeIntersect.current + ) + ) { + updateWidgetPosition(selectedZone, rightClickSelected, [ + selectedWidget.position[0], + planeIntersect.current.y, + selectedWidget.position[2], + ]); + } + } + + if (rightSelect === "RotateX") { + const deltaX = event.clientX - mouseStartRef.current.x; + const rotationSpeed = 0.03; + const newRotation: [number, number, number] = [ + rotationStartRef.current[0] + deltaX * rotationSpeed, + rotationStartRef.current[1], + rotationStartRef.current[2], + ]; + updateWidgetRotation(selectedZone, rightClickSelected, newRotation); + } + + if (rightSelect === "RotateY") { + const deltaY = event.clientY - mouseStartRef.current.y; + const rotationSpeed = 0.03; + const newRotation: [number, number, number] = [ + rotationStartRef.current[0], + rotationStartRef.current[1] + deltaY * rotationSpeed, + rotationStartRef.current[2], + ]; + updateWidgetRotation(selectedZone, rightClickSelected, newRotation); + } + if (rightSelect === "RotateZ") { + const deltaX = event.movementX; + const rotationSpeed = 0.03; + const currentRotation = selectedWidget.rotation || [0, 0, 0]; + const newRotation: [number, number, number] = [ + currentRotation[0], + currentRotation[1], + currentRotation[2] + deltaX * rotationSpeed, + ]; + updateWidgetRotation(selectedZone, rightClickSelected, newRotation); + } + }; + + const handleMouseUp = () => { + if ( + rightClickSelected && + (rightSelect === "Horizontal Move" || + rightSelect === "Vertical Move" || + rightSelect === "RotateX" || + rightSelect === "RotateY" || + rightSelect === "RotateZ") + ) { + setTimeout(() => { + setRightClickSelected(null); + setRightSelect(null); + }, 50); + } + }; + + window.addEventListener("mousedown", handleMouseDown); + window.addEventListener("mousemove", handleMouseMove); + window.addEventListener("mouseup", handleMouseUp); + + return () => { + window.removeEventListener("mousedown", handleMouseDown); + window.removeEventListener("mousemove", handleMouseMove); + window.removeEventListener("mouseup", handleMouseUp); + }; + }, [rightClickSelected, rightSelect, zoneWidgetData, gl]); + + return ( + <> + {activeZoneWidgets.map( + ({ id, type, position, rotation = [0, 0, 0] }: WidgetData) => { + const handleRightClick = (event: React.MouseEvent, id: string) => { event.preventDefault(); - - 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") + const canvasElement = document.getElementById( + "real-time-vis-canvas" ); + if (!canvasElement) throw new Error("Canvas element not found"); + const canvasRect = canvasElement.getBoundingClientRect(); + const relativeX = event.clientX - canvasRect.left; + const relativeY = event.clientY - canvasRect.top; + setEditWidgetOptions(true); + setRightClickSelected(id); - if (intersects.length > 0) { - const { x, y, z } = intersects[0].point; - const newWidget: WidgetData = { - id: generateUniqueId(), - type: widgetSelect, - position: [x, y, z], - rotation: [0, 0, 0], - }; + setTop(relativeY); + setLeft(relativeX); + }; - const add3dWidget = { - organization: organization, - widget: newWidget, - zoneId: selectedZone.zoneId - }; - - if (visualizationSocket) { - visualizationSocket.emit("v2:viz-3D-widget:add", add3dWidget); - } - - addWidget(selectedZone.zoneId, newWidget); - } - }; - - canvasElement.addEventListener("drop", onDrop); - return () => { - canvasElement.removeEventListener("drop", onDrop); - }; - }, [widgetSelect, activeModule, selectedZone.zoneId, widgetSubOption]); - - const activeZoneWidgets = zoneWidgetData[selectedZone.zoneId] || []; - - useEffect(() => { - if (!rightClickSelected) return; - const email = localStorage.getItem("email") || ""; - const organization = email?.split("@")[1]?.split(".")[0]; - - if (rightSelect === "Duplicate") { - const widgetToDuplicate = activeZoneWidgets.find((w: WidgetData) => w.id === rightClickSelected); - if (!widgetToDuplicate) return; - const newWidget: WidgetData = { - id: generateUniqueId(), - type: widgetToDuplicate.type, - position: [ - widgetToDuplicate.position[0] + 0.5, - widgetToDuplicate.position[1], - widgetToDuplicate.position[2] + 0.5, - ], - rotation: widgetToDuplicate.rotation || [0, 0, 0], - }; - - const add3dWidget = { - organization, - widget: newWidget, - zoneId: selectedZone.zoneId - }; - - addWidget(selectedZone.zoneId, newWidget); - setRightSelect(null); - setRightClickSelected(null); + switch (type) { + case "ui-Widget 1": + return ( + handleRightClick(e, id)} + /> + ); + case "ui-Widget 2": + return ( + handleRightClick(e, id)} + /> + ); + case "ui-Widget 3": + return ( + handleRightClick(e, id)} + /> + ); + case "ui-Widget 4": + return ( + handleRightClick(e, id)} + /> + ); + default: + return null; + } } - - if (rightSelect === "Delete") { - const deleteWidget = { - organization, - widgetId: rightClickSelected, - zoneId: selectedZone.zoneId - }; - - setZoneWidgetData( - selectedZone.zoneId, - activeZoneWidgets.filter((w: WidgetData) => w.id !== rightClickSelected) - ); - setRightClickSelected(null); - setRightSelect(null); - } - }, [rightSelect, rightClickSelected]); - - useEffect(() => { - const handleMouseDown = (event: MouseEvent) => { - if (!rightClickSelected || !rightSelect) return; - - if (rightSelect === "RotateX" || rightSelect === "RotateY") { - mouseStartRef.current = { x: event.clientX, y: event.clientY }; - - const selectedZone = Object.keys(zoneWidgetData).find((zoneId: string) => - zoneWidgetData[zoneId].some((widget: WidgetData) => widget.id === rightClickSelected) - ); - - if (!selectedZone) return; - - const selectedWidget = zoneWidgetData[selectedZone].find((widget: WidgetData) => widget.id === rightClickSelected); - if (selectedWidget) { - rotationStartRef.current = selectedWidget.rotation || [0, 0, 0]; - } - } - }; - - const handleMouseMove = (event: MouseEvent) => { - if (!rightClickSelected || !rightSelect) return; - const selectedZone = Object.keys(zoneWidgetData).find((zoneId: string) => - zoneWidgetData[zoneId].some((widget: WidgetData) => widget.id === rightClickSelected) - ); - if (!selectedZone) return; - - const selectedWidget = zoneWidgetData[selectedZone].find((widget: WidgetData) => widget.id === rightClickSelected); - if (!selectedWidget) return; - - const rect = gl.domElement.getBoundingClientRect(); - mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1; - mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1; - - raycaster.setFromCamera(mouse, camera); - - if (rightSelect === "Horizontal Move" && raycaster.ray.intersectPlane(plane.current, planeIntersect.current)) { - const newPosition: [number, number, number] = [ - planeIntersect.current.x, - selectedWidget.position[1], - planeIntersect.current.z - ]; - updateWidgetPosition(selectedZone, rightClickSelected, newPosition); - console.log('Horizontal Move - Final Position:', newPosition); - } - - if (rightSelect === "Vertical Move") { - if (raycaster.ray.intersectPlane(verticalPlane.current, planeIntersect.current)) { - updateWidgetPosition(selectedZone, rightClickSelected, [ - selectedWidget.position[0], - planeIntersect.current.y, - selectedWidget.position[2] - ]); - } - } - - if (rightSelect === "RotateX") { - const deltaX = event.clientX - mouseStartRef.current.x; - const rotationSpeed = 0.03; - const newRotation: [number, number, number] = [ - rotationStartRef.current[0] + deltaX * rotationSpeed, - rotationStartRef.current[1], - rotationStartRef.current[2] - ]; - updateWidgetRotation(selectedZone, rightClickSelected, newRotation); - } - - if (rightSelect === "RotateY") { - const deltaY = event.clientY - mouseStartRef.current.y; - const rotationSpeed = 0.03; - const newRotation: [number, number, number] = [ - rotationStartRef.current[0], - rotationStartRef.current[1] + deltaY * rotationSpeed, - rotationStartRef.current[2] - ]; - updateWidgetRotation(selectedZone, rightClickSelected, newRotation); - } - if (rightSelect === "RotateZ") { - const deltaX = event.movementX; - const rotationSpeed = 0.03; - const currentRotation = selectedWidget.rotation || [0, 0, 0]; - const newRotation: [number, number, number] = [ - currentRotation[0], - currentRotation[1], - currentRotation[2] + deltaX * rotationSpeed - ]; - updateWidgetRotation(selectedZone, rightClickSelected, newRotation); - } - - }; - - const handleMouseUp = () => { - if (rightClickSelected && ( - rightSelect === "Horizontal Move" || - rightSelect === "Vertical Move" || - rightSelect === "RotateX" || - rightSelect === "RotateY" || rightSelect === "RotateZ" - )) { - setTimeout(() => { - setRightClickSelected(null); - setRightSelect(null); - }, 50); - } - }; - - window.addEventListener("mousedown", handleMouseDown); - window.addEventListener("mousemove", handleMouseMove); - window.addEventListener("mouseup", handleMouseUp); - - return () => { - window.removeEventListener("mousedown", handleMouseDown); - window.removeEventListener("mousemove", handleMouseMove); - window.removeEventListener("mouseup", handleMouseUp); - }; - }, [rightClickSelected, rightSelect, zoneWidgetData, gl]); - - return ( - <> - {activeZoneWidgets.map(({ id, type, position, rotation = [0, 0, 0] }: WidgetData) => { - const handleRightClick = (event: React.MouseEvent, id: string) => { - event.preventDefault(); - const canvasElement = document.getElementById("real-time-vis-canvas"); - if (!canvasElement) throw new Error("Canvas element not found"); - const canvasRect = canvasElement.getBoundingClientRect(); - const relativeX = event.clientX - canvasRect.left; - const relativeY = event.clientY - canvasRect.top; - setRightClickSelected(id); - setTop(relativeY); - setLeft(relativeX); - }; - - switch (type) { - case "ui-Widget 1": - return ( - handleRightClick(e, id)} - /> - ); - case "ui-Widget 2": - return ( - handleRightClick(e, id)} - /> - ); - case "ui-Widget 3": - return ( - handleRightClick(e, id)} - /> - ); - case "ui-Widget 4": - return ( - handleRightClick(e, id)} - /> - ); - default: - return null; - } - })} - - ); -} \ No newline at end of file + )} + + ); +} diff --git a/app/src/components/ui/componets/RealTimeVisulization.tsx b/app/src/components/ui/componets/RealTimeVisulization.tsx index acbbf68..4b8d7c8 100644 --- a/app/src/components/ui/componets/RealTimeVisulization.tsx +++ b/app/src/components/ui/componets/RealTimeVisulization.tsx @@ -7,7 +7,6 @@ import DisplayZone from "./DisplayZone"; import Scene from "../../../modules/scene/scene"; import useModuleStore from "../../../store/useModuleStore"; - import { useDroppedObjectsStore } from "../../../store/useDroppedObjectsStore"; import { useAsset3dWidget, @@ -24,7 +23,11 @@ import RenderOverlay from "../../templates/Overlay"; import ConfirmationPopup from "../../layout/confirmationPopup/ConfirmationPopup"; import DroppedObjects from "./DroppedFloatingWidgets"; import EditWidgetOption from "../menu/EditWidgetOption"; -import { useRightClickSelected } from "../../../store/useZone3DWidgetStore"; +import { + useRightClickSelected, + useRightSelected, + useEditWidgetOptionsStore, +} from "../../../store/useZone3DWidgetStore"; type Side = "top" | "bottom" | "left" | "right"; @@ -57,9 +60,9 @@ const RealTimeVisulization: React.FC = () => { const [droppedObjects, setDroppedObjects] = useState([]); const [zonesData, setZonesData] = useState({}); const { selectedZone, setSelectedZone } = useSelectedZoneStore(); - - - const { rightClickSelected, setRightClickSelected } = useRightClickSelected() + + const { editWidgetOptions, setEditWidgetOptions } = useEditWidgetOptionsStore(); + const { rightClickSelected, setRightClickSelected } = useRightClickSelected(); const [openConfirmationPopup, setOpenConfirmationPopup] = useState(false); const [floatingWidgets, setFloatingWidgets] = useState< @@ -68,6 +71,7 @@ const RealTimeVisulization: React.FC = () => { const { widgetSelect, setWidgetSelect } = useAsset3dWidget(); const { widgetSubOption, setWidgetSubOption } = useWidgetSubOption(); const { visualizationSocket } = useSocketStore(); + const { setRightSelect } = useRightSelected(); useEffect(() => { async function GetZoneData() { @@ -96,7 +100,7 @@ const RealTimeVisulization: React.FC = () => { {} ); setZonesData(formattedData); - } catch (error) { } + } catch (error) {} } GetZoneData(); @@ -142,8 +146,8 @@ const RealTimeVisulization: React.FC = () => { const relativeX = event.clientX - canvasRect.left; const relativeY = event.clientY - canvasRect.top; - const newPosition = determinePosition(canvasRect, relativeX, relativeY) - console.log('newPosition: ', newPosition); + const newPosition = determinePosition(canvasRect, relativeX, relativeY); + console.log("newPosition: ", newPosition); const newObject = { ...droppedData, id: generateUniqueId(), @@ -162,12 +166,12 @@ const RealTimeVisulization: React.FC = () => { let addFloatingWidget = { organization: organization, widget: newObject, - zoneId: selectedZone.zoneId - } - console.log('newObject: ', newObject); + zoneId: selectedZone.zoneId, + }; + console.log("newObject: ", newObject); if (visualizationSocket) { - visualizationSocket.emit("v2:viz-float:add", addFloatingWidget) + visualizationSocket.emit("v2:viz-float:add", addFloatingWidget); } // let response = await addingFloatingWidgets( @@ -194,9 +198,30 @@ const RealTimeVisulization: React.FC = () => { ], }, })); - } catch (error) { } + } catch (error) {} }; + // Add this useEffect hook to your component + useEffect(() => { + const handleClickOutside = (event: MouseEvent) => { + const editWidgetOptions = document.querySelector( + ".editWidgetOptions-wrapper" + ); + if ( + editWidgetOptions && + !editWidgetOptions.contains(event.target as Node) + ) { + setRightClickSelected(null); + setRightSelect(null); + } + }; + + document.addEventListener("mousedown", handleClickOutside); + return () => { + document.removeEventListener("mousedown", handleClickOutside); + }; + }, [setRightClickSelected]); + return (
{ >
- {activeModule === "visualization" && selectedZone.zoneName !== "" && } + {activeModule === "visualization" && selectedZone.zoneName !== "" && ( + + )} {activeModule === "visualization" && } - {activeModule === "visualization" && rightClickSelected && } + {activeModule === "visualization" && + editWidgetOptions && + rightClickSelected && ( + + )} {activeModule === "visualization" && ( <> diff --git a/app/src/components/ui/menu/EditWidgetOption.tsx b/app/src/components/ui/menu/EditWidgetOption.tsx index 3978430..f1d6263 100644 --- a/app/src/components/ui/menu/EditWidgetOption.tsx +++ b/app/src/components/ui/menu/EditWidgetOption.tsx @@ -1,34 +1,51 @@ import React, { useEffect } from "react"; -import { useLeftData, useRightSelected, useTopData } from "../../../store/useZone3DWidgetStore"; +import { + useEditWidgetOptionsStore, + useLeftData, + useRightClickSelected, + useRightSelected, + useTopData, +} from "../../../store/useZone3DWidgetStore"; interface EditWidgetOptionProps { + setWidgetSelect: any; options: string[]; } -const EditWidgetOption: React.FC = ({ options }) => { - const { top, setTop } = useTopData() - const { left, setLeft } = useLeftData() - const { rightSelect, setRightSelect } = useRightSelected() +const EditWidgetOption: React.FC = ({ + setWidgetSelect, + options, +}) => { + const { top, setTop } = useTopData(); + const { left, setLeft } = useLeftData(); + const { rightSelect, setRightSelect } = useRightSelected(); + const { setEditWidgetOptions } = useEditWidgetOptionsStore(); useEffect(() => { - - console.log('left: ', left); - console.log('top: ', top); - }, [top, left]) + console.log("left: ", left); + console.log("top: ", top); + }, [top, left]); return ( -
{options.map((option, index) => ( -
setRightSelect(option)}> +
{ + setRightSelect(option); + setEditWidgetOptions(false); + }} + > {option}
))} diff --git a/app/src/store/useZone3DWidgetStore.ts b/app/src/store/useZone3DWidgetStore.ts index 9700878..425674f 100644 --- a/app/src/store/useZone3DWidgetStore.ts +++ b/app/src/store/useZone3DWidgetStore.ts @@ -96,3 +96,14 @@ export const useRightSelected = create((set) => ({ rightSelect: null, // Default state is null setRightSelect: (x) => set({ rightSelect: x }), })); + + +interface EditWidgetOptionsStore { + editWidgetOptions: boolean; + setEditWidgetOptions: (value: boolean) => void; +} + +export const useEditWidgetOptionsStore = create((set) => ({ + editWidgetOptions: false, // Initial state + setEditWidgetOptions: (value: boolean) => set({ editWidgetOptions: value }), +})); diff --git a/app/src/styles/pages/realTimeViz.scss b/app/src/styles/pages/realTimeViz.scss index a79823c..74ef66e 100644 --- a/app/src/styles/pages/realTimeViz.scss +++ b/app/src/styles/pages/realTimeViz.scss @@ -719,9 +719,9 @@ .editWidgetOptions { position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); + // top: 50%; + // left: 50%; + // transform: translate(-50%, -50%); background-color: var(--background-color); z-index: 3; display: flex; @@ -729,6 +729,7 @@ border-radius: 6px; overflow: hidden; +min-width: 150px; .option { padding: 8px 10px; color: var(--text-color); From baed3cd98b1b2613f24b0fbb894a3b4d594f0f4f Mon Sep 17 00:00:00 2001 From: Gomathi9520 Date: Fri, 4 Apr 2025 18:47:47 +0530 Subject: [PATCH 4/4] 3d editoption api added and rotation functionality added --- .../3D-cards/cards/ProductionCapacity.tsx | 7 +- .../3D-cards/cards/ReturnOfInvestment.tsx | 21 +- .../layout/3D-cards/cards/StateWorking.tsx | 20 +- .../layout/3D-cards/cards/Throughput.tsx | 21 +- .../visualization/widgets/Widgets2D.tsx | 2 +- .../properties/GlobalProperties.tsx | 2 +- .../FleetEfficiencyInputComponent.tsx | 2 +- .../IotInputCards/FlotingWidgetInput.tsx | 2 +- .../WarehouseThroughputInputComponent.tsx | 2 +- .../ui/componets/Dropped3dWidget.tsx | 760 +++++++++--------- .../ui/componets/DroppedFloatingWidgets.tsx | 60 +- .../ui/componets/RealTimeVisulization.tsx | 42 +- .../components/ui/menu/EditWidgetOption.tsx | 11 +- .../realTimeVis/charts/BarGraphComponent.tsx | 2 +- .../charts/DoughnutGraphComponent.tsx | 2 +- .../realTimeVis/charts/LineGraphComponent.tsx | 2 +- .../realTimeVis/charts/PieGraphComponent.tsx | 2 +- .../charts/PolarAreaGraphComponent.tsx | 2 +- .../zoneData/add3dWidget.ts | 3 - .../zoneData/delete3dWidget.ts | 38 + .../zoneData/update3dWidget.ts | 81 ++ app/src/store/useZone3DWidgetStore.ts | 1 - 22 files changed, 637 insertions(+), 448 deletions(-) create mode 100644 app/src/services/realTimeVisulization/zoneData/delete3dWidget.ts create mode 100644 app/src/services/realTimeVisulization/zoneData/update3dWidget.ts diff --git a/app/src/components/layout/3D-cards/cards/ProductionCapacity.tsx b/app/src/components/layout/3D-cards/cards/ProductionCapacity.tsx index 0d239b4..a719023 100644 --- a/app/src/components/layout/3D-cards/cards/ProductionCapacity.tsx +++ b/app/src/components/layout/3D-cards/cards/ProductionCapacity.tsx @@ -157,10 +157,10 @@ const ProductionCapacity: React.FC = ({ id, type, posit setDuration(response.data.Data.duration) setName(response.data.widgetName) } else { - console.log("Unexpected response:", response); + } } catch (error) { - console.error("There was an error!", error); + } } } @@ -178,7 +178,7 @@ const ProductionCapacity: React.FC = ({ id, type, posit useEffect(() => { - console.log('rotation: ', rotation); + }, [rotation]) const rotationDegrees = { x: (rotation[0] * 180) / Math.PI, @@ -202,7 +202,6 @@ const ProductionCapacity: React.FC = ({ id, type, posit transform: transformStyle.transform, transformStyle: 'preserve-3d', transition: 'transform 0.1s ease-out' - }}>
setSelectedChartId({ id: id, type: type })} diff --git a/app/src/components/layout/3D-cards/cards/ReturnOfInvestment.tsx b/app/src/components/layout/3D-cards/cards/ReturnOfInvestment.tsx index 96fc305..8e6c707 100644 --- a/app/src/components/layout/3D-cards/cards/ReturnOfInvestment.tsx +++ b/app/src/components/layout/3D-cards/cards/ReturnOfInvestment.tsx @@ -43,9 +43,10 @@ interface ReturnOfInvestmentProps { id: string; type: string; position: [number, number, number]; + rotation: [number, number, number]; onContextMenu?: (event: React.MouseEvent) => void; } -const ReturnOfInvestment: React.FC = ({ id, type, position, onContextMenu }) => { +const ReturnOfInvestment: React.FC = ({ id, type, position, rotation, onContextMenu }) => { const { selectedChartId, setSelectedChartId } = useWidgetStore(); const { measurements: chartMeasurements, duration: chartDuration, name: widgetName } = useChartStore(); @@ -203,13 +204,29 @@ const ReturnOfInvestment: React.FC = ({ id, type, posit } } , [chartMeasurements, chartDuration, widgetName]) + const rotationDegrees = { + x: (rotation[0] * 180) / Math.PI, + y: (rotation[1] * 180) / Math.PI, + z: (rotation[2] * 180) / Math.PI, + }; + + const transformStyle = { + transform: `rotateX(${rotationDegrees.x}deg) rotateY(${rotationDegrees.y}deg) rotateZ(${rotationDegrees.z}deg)`, + }; return ( + sprite + style={{ + transform: transformStyle.transform, + transformStyle: 'preserve-3d', + transition: 'transform 0.1s ease-out' + + }} + >
setSelectedChartId({ id: id, type: type })} onContextMenu={onContextMenu} diff --git a/app/src/components/layout/3D-cards/cards/StateWorking.tsx b/app/src/components/layout/3D-cards/cards/StateWorking.tsx index efd4e7d..287bc15 100644 --- a/app/src/components/layout/3D-cards/cards/StateWorking.tsx +++ b/app/src/components/layout/3D-cards/cards/StateWorking.tsx @@ -10,9 +10,10 @@ interface StateWorkingProps { id: string; type: string; position: [number, number, number]; + rotation: [number, number, number]; onContextMenu?: (event: React.MouseEvent) => void; } -const StateWorking: React.FC = ({ id, type, position, onContextMenu }) => { +const StateWorking: React.FC = ({ id, type, position, rotation, onContextMenu }) => { const { selectedChartId, setSelectedChartId } = useWidgetStore(); const { measurements: chartMeasurements, duration: chartDuration, name: widgetName } = useChartStore(); const [measurements, setmeasurements] = useState({}); @@ -89,13 +90,28 @@ const StateWorking: React.FC = ({ id, type, position, onConte } , [chartMeasurements, chartDuration, widgetName]) + const rotationDegrees = { + x: (rotation[0] * 180) / Math.PI, + y: (rotation[1] * 180) / Math.PI, + z: (rotation[2] * 180) / Math.PI, + }; + const transformStyle = { + transform: `rotateX(${rotationDegrees.x}deg) rotateY(${rotationDegrees.y}deg) rotateZ(${rotationDegrees.z}deg)`, + }; return ( + sprite + style={{ + transform: transformStyle.transform, + transformStyle: 'preserve-3d', + transition: 'transform 0.1s ease-out' + + }} + >
setSelectedChartId({ id: id, type: type })} onContextMenu={onContextMenu} diff --git a/app/src/components/layout/3D-cards/cards/Throughput.tsx b/app/src/components/layout/3D-cards/cards/Throughput.tsx index 5d6fab9..de3109b 100644 --- a/app/src/components/layout/3D-cards/cards/Throughput.tsx +++ b/app/src/components/layout/3D-cards/cards/Throughput.tsx @@ -45,10 +45,11 @@ interface ThroughputProps { id: string; type: string; position: [number, number, number]; + rotation: [number, number, number]; onContextMenu?: (event: React.MouseEvent) => void; } -const Throughput: React.FC = ({ id, type, position, onContextMenu }) => { +const Throughput: React.FC = ({ id, type, position, rotation, onContextMenu }) => { const { selectedChartId, setSelectedChartId } = useWidgetStore(); const { measurements: chartMeasurements, duration: chartDuration, name: widgetName } = useChartStore(); @@ -183,13 +184,29 @@ const Throughput: React.FC = ({ id, type, position, onContextMe } } , [chartMeasurements, chartDuration, widgetName]) + const rotationDegrees = { + x: (rotation[0] * 180) / Math.PI, + y: (rotation[1] * 180) / Math.PI, + z: (rotation[2] * 180) / Math.PI, + }; + + const transformStyle = { + transform: `rotateX(${rotationDegrees.x}deg) rotateY(${rotationDegrees.y}deg) rotateZ(${rotationDegrees.z}deg)`, + }; return ( + sprite + style={{ + transform: transformStyle.transform, + transformStyle: 'preserve-3d', + transition: 'transform 0.1s ease-out' + + }} + >
setSelectedChartId({ id: id, type: type })} onContextMenu={onContextMenu} diff --git a/app/src/components/layout/sidebarLeft/visualization/widgets/Widgets2D.tsx b/app/src/components/layout/sidebarLeft/visualization/widgets/Widgets2D.tsx index 561c82b..9f71423 100644 --- a/app/src/components/layout/sidebarLeft/visualization/widgets/Widgets2D.tsx +++ b/app/src/components/layout/sidebarLeft/visualization/widgets/Widgets2D.tsx @@ -101,7 +101,7 @@ const ProgressBarWidget = ({
); }; -console.log(chartTypes, "chartTypes"); + const Widgets2D = () => { return ( diff --git a/app/src/components/layout/sidebarRight/properties/GlobalProperties.tsx b/app/src/components/layout/sidebarRight/properties/GlobalProperties.tsx index 4e6a537..4ffea3b 100644 --- a/app/src/components/layout/sidebarRight/properties/GlobalProperties.tsx +++ b/app/src/components/layout/sidebarRight/properties/GlobalProperties.tsx @@ -33,7 +33,7 @@ const GlobalProperties: React.FC = () => { const { setPlaneValue, setGridValue, planeValue, gridValue } = useTileDistance(); useEffect(() => { - console.log(gridValue, planeValue, "values"); + }, [gridValue, planeValue]); const { socket } = useSocketStore(); const { limitDistance, setLimitDistance } = useLimitDistance(); diff --git a/app/src/components/layout/sidebarRight/visualization/IotInputCards/FleetEfficiencyInputComponent.tsx b/app/src/components/layout/sidebarRight/visualization/IotInputCards/FleetEfficiencyInputComponent.tsx index cd60d24..55ae422 100644 --- a/app/src/components/layout/sidebarRight/visualization/IotInputCards/FleetEfficiencyInputComponent.tsx +++ b/app/src/components/layout/sidebarRight/visualization/IotInputCards/FleetEfficiencyInputComponent.tsx @@ -45,7 +45,7 @@ const FleetEfficiencyInputComponent = (props: Props) => { try { const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/A_floatWidget/${selectedChartId.id}/${organization}`); if (response.status === 200) { - console.log(response.data); + setSelections(response.data.Data.measurements) setDuration(response.data.Data.duration) diff --git a/app/src/components/layout/sidebarRight/visualization/IotInputCards/FlotingWidgetInput.tsx b/app/src/components/layout/sidebarRight/visualization/IotInputCards/FlotingWidgetInput.tsx index e389802..1164a84 100644 --- a/app/src/components/layout/sidebarRight/visualization/IotInputCards/FlotingWidgetInput.tsx +++ b/app/src/components/layout/sidebarRight/visualization/IotInputCards/FlotingWidgetInput.tsx @@ -45,7 +45,7 @@ const FlotingWidgetInput = (props: Props) => { try { const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/A_floatWidget/${selectedChartId.id}/${organization}`); if (response.status === 200) { - console.log(response.data); + setSelections(response.data.Data.measurements) setDuration(response.data.Data.duration) diff --git a/app/src/components/layout/sidebarRight/visualization/IotInputCards/WarehouseThroughputInputComponent.tsx b/app/src/components/layout/sidebarRight/visualization/IotInputCards/WarehouseThroughputInputComponent.tsx index c35211e..d3ed377 100644 --- a/app/src/components/layout/sidebarRight/visualization/IotInputCards/WarehouseThroughputInputComponent.tsx +++ b/app/src/components/layout/sidebarRight/visualization/IotInputCards/WarehouseThroughputInputComponent.tsx @@ -45,7 +45,7 @@ const WarehouseThroughputInputComponent = (props: Props) => { try { const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/A_floatWidget/${selectedChartId.id}/${organization}`); if (response.status === 200) { - console.log(response.data); + setSelections(response.data.Data.measurements) setDuration(response.data.Data.duration) diff --git a/app/src/components/ui/componets/Dropped3dWidget.tsx b/app/src/components/ui/componets/Dropped3dWidget.tsx index 7885275..567aa7a 100644 --- a/app/src/components/ui/componets/Dropped3dWidget.tsx +++ b/app/src/components/ui/componets/Dropped3dWidget.tsx @@ -1,10 +1,6 @@ import { useThree } from "@react-three/fiber"; import React, { useEffect, useRef } from "react"; -import { - useAsset3dWidget, - useSocketStore, - useWidgetSubOption, -} from "../../../store/store"; +import { useAsset3dWidget, useSocketStore, useWidgetSubOption } from "../../../store/store"; import useModuleStore from "../../../store/useModuleStore"; import { ThreeState } from "../../../types/world/worldTypes"; import * as THREE from "three"; @@ -17,391 +13,413 @@ import { generateUniqueId } from "../../../functions/generateUniqueId"; import { adding3dWidgets } from "../../../services/realTimeVisulization/zoneData/add3dWidget"; import { get3dWidgetZoneData } from "../../../services/realTimeVisulization/zoneData/get3dWidgetData"; import { use3DWidget } from "../../../store/useDroppedObjectsStore"; -import { - useEditWidgetOptionsStore, - useLeftData, - useRightClickSelected, - useRightSelected, - useTopData, - useZoneWidgetStore, -} from "../../../store/useZone3DWidgetStore"; +import { useEditWidgetOptionsStore, useLeftData, useRightClickSelected, useRightSelected, useTopData, useZoneWidgetStore } from "../../../store/useZone3DWidgetStore"; import { useWidgetStore } from "../../../store/useWidgetStore"; import EditWidgetOption from "../menu/EditWidgetOption"; +import { delete3dWidgetApi } from "../../../services/realTimeVisulization/zoneData/delete3dWidget"; +import { update3dWidget, update3dWidgetRotation } from "../../../services/realTimeVisulization/zoneData/update3dWidget"; type WidgetData = { - id: string; - type: string; - position: [number, number, number]; - rotation?: [number, number, number]; - tempPosition?: [number, number, number]; + id: string; + type: string; + position: [number, number, number]; + rotation?: [number, number, number]; + tempPosition?: [number, number, number]; }; + export default function Dropped3dWidgets() { - const { widgetSelect } = useAsset3dWidget(); - const { activeModule } = useModuleStore(); - const { raycaster, gl, scene, mouse, camera }: ThreeState = useThree(); - const { widgetSubOption } = useWidgetSubOption(); - const { selectedZone } = useSelectedZoneStore(); - const { top, setTop } = useTopData(); - const { left, setLeft } = useLeftData(); - const { rightSelect, setRightSelect } = useRightSelected(); - const { setEditWidgetOptions } = useEditWidgetOptionsStore(); + const { widgetSelect } = useAsset3dWidget(); + const { activeModule } = useModuleStore(); + const { raycaster, gl, scene, mouse, camera }: ThreeState = useThree(); + const { widgetSubOption } = useWidgetSubOption(); + const { selectedZone } = useSelectedZoneStore(); + const { top, setTop } = useTopData(); + const { left, setLeft } = useLeftData(); + const { rightSelect, setRightSelect } = useRightSelected(); + const { editWidgetOptions, setEditWidgetOptions } = useEditWidgetOptionsStore() + const { zoneWidgetData, setZoneWidgetData, addWidget, updateWidgetPosition, updateWidgetRotation } = useZoneWidgetStore(); + const { setWidgets3D } = use3DWidget(); + const { visualizationSocket } = useSocketStore(); + const { rightClickSelected, setRightClickSelected } = useRightClickSelected(); + const plane = useRef(new THREE.Plane(new THREE.Vector3(0, 1, 0), 0)); // Floor plane for horizontal move + const verticalPlane = useRef(new THREE.Plane(new THREE.Vector3(0, 0, 1), 0)); // Vertical plane for vertical move + const planeIntersect = useRef(new THREE.Vector3()); + // const plane = useRef(new THREE.Plane(new THREE.Vector3(0, 1, 0), 0); + // const verticalPlane = useRef(new THREE.Plane(new THREE.Vector3(0, 0, 1), 0); + // const planeIntersect = useRef(new THREE.Vector3()); + const rotationStartRef = useRef<[number, number, number]>([0, 0, 0]); + const mouseStartRef = useRef<{ x: number; y: number }>({ x: 0, y: 0 }); - const { - zoneWidgetData, - setZoneWidgetData, - addWidget, - updateWidgetPosition, - updateWidgetRotation, - } = useZoneWidgetStore(); - const { setWidgets3D } = use3DWidget(); - const { visualizationSocket } = useSocketStore(); - const { rightClickSelected, setRightClickSelected } = useRightClickSelected(); - const plane = useRef(new THREE.Plane(new THREE.Vector3(0, 1, 0), 0)); // Floor plane for horizontal move - const verticalPlane = useRef(new THREE.Plane(new THREE.Vector3(0, 0, 1), 0)); // Vertical plane for vertical move - const planeIntersect = useRef(new THREE.Vector3()); - // const plane = useRef(new THREE.Plane(new THREE.Vector3(0, 1, 0), 0); - // const verticalPlane = useRef(new THREE.Plane(new THREE.Vector3(0, 0, 1), 0); - // const planeIntersect = useRef(new THREE.Vector3()); - const rotationStartRef = useRef<[number, number, number]>([0, 0, 0]); - const mouseStartRef = useRef<{ x: number; y: number }>({ x: 0, y: 0 }); + useEffect(() => { + if (activeModule !== "visualization") return; + if (!selectedZone.zoneId) return; - useEffect(() => { - if (activeModule !== "visualization") return; - if (!selectedZone.zoneId) 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() { + const result = await get3dWidgetZoneData(selectedZone.zoneId, organization); + console.log('result: ', result); + setWidgets3D(result); - async function get3dWidgetData() { - const result = await get3dWidgetZoneData( - selectedZone.zoneId, - organization - ); - setWidgets3D(result); + const formattedWidgets = result.map((widget: WidgetData) => ({ + id: widget.id, + type: widget.type, + position: widget.position, + rotation: widget.rotation || [0, 0, 0], + })); - const formattedWidgets = result.map((widget: WidgetData) => ({ - id: widget.id, - type: widget.type, - position: widget.position, - rotation: widget.rotation || [0, 0, 0], - })); - setZoneWidgetData(selectedZone.zoneId, formattedWidgets); - } - - get3dWidgetData(); - }, [selectedZone.zoneId, activeModule]); - - useEffect(() => { - if (activeModule !== "visualization") return; - if (widgetSubOption === "Floating" || widgetSubOption === "2D") return; - if (selectedZone.zoneName === "") return; - - const canvasElement = gl.domElement; - - const onDrop = async (event: DragEvent) => { - event.preventDefault(); - - 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; - const newWidget: WidgetData = { - id: generateUniqueId(), - type: widgetSelect, - position: [x, y, z], - rotation: [0, 0, 0], - }; - - const add3dWidget = { - organization: organization, - widget: newWidget, - zoneId: selectedZone.zoneId, - }; - - if (visualizationSocket) { - visualizationSocket.emit("v2:viz-3D-widget:add", add3dWidget); + setZoneWidgetData(selectedZone.zoneId, formattedWidgets); } - addWidget(selectedZone.zoneId, newWidget); - } - }; + get3dWidgetData(); + }, [selectedZone.zoneId, activeModule]); - canvasElement.addEventListener("drop", onDrop); - return () => { - canvasElement.removeEventListener("drop", onDrop); - }; - }, [widgetSelect, activeModule, selectedZone.zoneId, widgetSubOption]); + useEffect(() => { + if (activeModule !== "visualization") return; + if (widgetSubOption === "Floating" || widgetSubOption === "2D") return; + if (selectedZone.zoneName === "") return; - const activeZoneWidgets = zoneWidgetData[selectedZone.zoneId] || []; + const canvasElement = gl.domElement; - useEffect(() => { - if (!rightClickSelected) return; - const email = localStorage.getItem("email") || ""; - const organization = email?.split("@")[1]?.split(".")[0]; - - if (rightSelect === "Duplicate") { - const widgetToDuplicate = activeZoneWidgets.find( - (w: WidgetData) => w.id === rightClickSelected - ); - if (!widgetToDuplicate) return; - const newWidget: WidgetData = { - id: generateUniqueId(), - type: widgetToDuplicate.type, - position: [ - widgetToDuplicate.position[0] + 0.5, - widgetToDuplicate.position[1], - widgetToDuplicate.position[2] + 0.5, - ], - rotation: widgetToDuplicate.rotation || [0, 0, 0], - }; - - const add3dWidget = { - organization, - widget: newWidget, - zoneId: selectedZone.zoneId, - }; - - addWidget(selectedZone.zoneId, newWidget); - setRightSelect(null); - setRightClickSelected(null); - } - - if (rightSelect === "Delete") { - const deleteWidget = { - organization, - widgetId: rightClickSelected, - zoneId: selectedZone.zoneId, - }; - - setZoneWidgetData( - selectedZone.zoneId, - activeZoneWidgets.filter((w: WidgetData) => w.id !== rightClickSelected) - ); - setRightClickSelected(null); - setRightSelect(null); - } - }, [rightSelect, rightClickSelected]); - - useEffect(() => { - const handleMouseDown = (event: MouseEvent) => { - if (!rightClickSelected || !rightSelect) return; - - if (rightSelect === "RotateX" || rightSelect === "RotateY") { - mouseStartRef.current = { x: event.clientX, y: event.clientY }; - - const selectedZone = Object.keys(zoneWidgetData).find( - (zoneId: string) => - zoneWidgetData[zoneId].some( - (widget: WidgetData) => widget.id === rightClickSelected - ) - ); - - if (!selectedZone) return; - - const selectedWidget = zoneWidgetData[selectedZone].find( - (widget: WidgetData) => widget.id === rightClickSelected - ); - if (selectedWidget) { - rotationStartRef.current = selectedWidget.rotation || [0, 0, 0]; - } - } - }; - - const handleMouseMove = (event: MouseEvent) => { - if (!rightClickSelected || !rightSelect) return; - const selectedZone = Object.keys(zoneWidgetData).find((zoneId: string) => - zoneWidgetData[zoneId].some( - (widget: WidgetData) => widget.id === rightClickSelected - ) - ); - if (!selectedZone) return; - - const selectedWidget = zoneWidgetData[selectedZone].find( - (widget: WidgetData) => widget.id === rightClickSelected - ); - if (!selectedWidget) return; - - const rect = gl.domElement.getBoundingClientRect(); - mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1; - mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1; - - raycaster.setFromCamera(mouse, camera); - - if ( - rightSelect === "Horizontal Move" && - raycaster.ray.intersectPlane(plane.current, planeIntersect.current) - ) { - const newPosition: [number, number, number] = [ - planeIntersect.current.x, - selectedWidget.position[1], - planeIntersect.current.z, - ]; - updateWidgetPosition(selectedZone, rightClickSelected, newPosition); - console.log("Horizontal Move - Final Position:", newPosition); - } - - if (rightSelect === "Vertical Move") { - if ( - raycaster.ray.intersectPlane( - verticalPlane.current, - planeIntersect.current - ) - ) { - updateWidgetPosition(selectedZone, rightClickSelected, [ - selectedWidget.position[0], - planeIntersect.current.y, - selectedWidget.position[2], - ]); - } - } - - if (rightSelect === "RotateX") { - const deltaX = event.clientX - mouseStartRef.current.x; - const rotationSpeed = 0.03; - const newRotation: [number, number, number] = [ - rotationStartRef.current[0] + deltaX * rotationSpeed, - rotationStartRef.current[1], - rotationStartRef.current[2], - ]; - updateWidgetRotation(selectedZone, rightClickSelected, newRotation); - } - - if (rightSelect === "RotateY") { - const deltaY = event.clientY - mouseStartRef.current.y; - const rotationSpeed = 0.03; - const newRotation: [number, number, number] = [ - rotationStartRef.current[0], - rotationStartRef.current[1] + deltaY * rotationSpeed, - rotationStartRef.current[2], - ]; - updateWidgetRotation(selectedZone, rightClickSelected, newRotation); - } - if (rightSelect === "RotateZ") { - const deltaX = event.movementX; - const rotationSpeed = 0.03; - const currentRotation = selectedWidget.rotation || [0, 0, 0]; - const newRotation: [number, number, number] = [ - currentRotation[0], - currentRotation[1], - currentRotation[2] + deltaX * rotationSpeed, - ]; - updateWidgetRotation(selectedZone, rightClickSelected, newRotation); - } - }; - - const handleMouseUp = () => { - if ( - rightClickSelected && - (rightSelect === "Horizontal Move" || - rightSelect === "Vertical Move" || - rightSelect === "RotateX" || - rightSelect === "RotateY" || - rightSelect === "RotateZ") - ) { - setTimeout(() => { - setRightClickSelected(null); - setRightSelect(null); - }, 50); - } - }; - - window.addEventListener("mousedown", handleMouseDown); - window.addEventListener("mousemove", handleMouseMove); - window.addEventListener("mouseup", handleMouseUp); - - return () => { - window.removeEventListener("mousedown", handleMouseDown); - window.removeEventListener("mousemove", handleMouseMove); - window.removeEventListener("mouseup", handleMouseUp); - }; - }, [rightClickSelected, rightSelect, zoneWidgetData, gl]); - - return ( - <> - {activeZoneWidgets.map( - ({ id, type, position, rotation = [0, 0, 0] }: WidgetData) => { - const handleRightClick = (event: React.MouseEvent, id: string) => { + const onDrop = async (event: DragEvent) => { event.preventDefault(); - const canvasElement = document.getElementById( - "real-time-vis-canvas" + + 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 (!canvasElement) throw new Error("Canvas element not found"); - const canvasRect = canvasElement.getBoundingClientRect(); - const relativeX = event.clientX - canvasRect.left; - const relativeY = event.clientY - canvasRect.top; - setEditWidgetOptions(true); - setRightClickSelected(id); - setTop(relativeY); - setLeft(relativeX); - }; + if (intersects.length > 0) { + const { x, y, z } = intersects[0].point; + const newWidget: WidgetData = { + id: generateUniqueId(), + type: widgetSelect, + position: [x, y, z], + rotation: [0, 0, 0], + }; - switch (type) { - case "ui-Widget 1": - return ( - handleRightClick(e, id)} - /> - ); - case "ui-Widget 2": - return ( - handleRightClick(e, id)} - /> - ); - case "ui-Widget 3": - return ( - handleRightClick(e, id)} - /> - ); - case "ui-Widget 4": - return ( - handleRightClick(e, id)} - /> - ); - default: - return null; - } + const add3dWidget = { + organization: organization, + widget: newWidget, + zoneId: selectedZone.zoneId + }; + + if (visualizationSocket) { + visualizationSocket.emit("v2:viz-3D-widget:add", add3dWidget); + } + + addWidget(selectedZone.zoneId, newWidget); + } + }; + + canvasElement.addEventListener("drop", onDrop); + return () => { + canvasElement.removeEventListener("drop", onDrop); + }; + }, [widgetSelect, activeModule, selectedZone.zoneId, widgetSubOption]); + + const activeZoneWidgets = zoneWidgetData[selectedZone.zoneId] || []; + + useEffect(() => { + if (!rightClickSelected) return; + const email = localStorage.getItem("email") || ""; + const organization = email?.split("@")[1]?.split(".")[0]; + + if (rightSelect === "Duplicate") { + async function duplicateWidget() { + + + const widgetToDuplicate = activeZoneWidgets.find((w: WidgetData) => w.id === rightClickSelected); + if (!widgetToDuplicate) return; + const newWidget: WidgetData = { + id: generateUniqueId(), + type: widgetToDuplicate.type, + position: [ + widgetToDuplicate.position[0] + 0.5, + widgetToDuplicate.position[1], + widgetToDuplicate.position[2] + 0.5, + ], + rotation: widgetToDuplicate.rotation || [0, 0, 0], + }; + const adding3dWidget = { + organization, + widget: newWidget, + zoneId: selectedZone.zoneId + }; + let response = await adding3dWidgets(selectedZone.zoneId, organization, newWidget) + console.log('response: ', response); + + addWidget(selectedZone.zoneId, newWidget); + setRightSelect(null); + setRightClickSelected(null); + } + duplicateWidget() } - )} - - ); -} + + if (rightSelect === "Delete") { + const deleteWidgetApi = async () => { + try { + const deleteWidget = { + organization, + widgetId: rightClickSelected, + zoneId: selectedZone.zoneId, + }; + + // Call the API to delete the widget + const response = await delete3dWidgetApi(selectedZone.zoneId, organization, rightClickSelected); + + + // if (response?.success) { + // Remove from state only if API call succeeds + setZoneWidgetData( + selectedZone.zoneId, + activeZoneWidgets.filter((w: WidgetData) => w.id !== rightClickSelected) + ); + // } else { + // console.error("Failed to delete widget:", response?.message); + // } + } catch (error) { + console.error("Error deleting widget:", error); + } finally { + setRightClickSelected(null); + setRightSelect(null); + } + }; + + deleteWidgetApi(); + } + }, [rightSelect, rightClickSelected]); + + useEffect(() => { + + const email = localStorage.getItem("email") || ""; + const organization = email?.split("@")[1]?.split(".")[0]; + const handleMouseDown = (event: MouseEvent) => { + if (!rightClickSelected || !rightSelect) return; + + if (rightSelect === "RotateX" || rightSelect === "RotateY") { + mouseStartRef.current = { x: event.clientX, y: event.clientY }; + + const selectedZone = Object.keys(zoneWidgetData).find((zoneId: string) => + zoneWidgetData[zoneId].some((widget: WidgetData) => widget.id === rightClickSelected) + ); + + if (!selectedZone) return; + + const selectedWidget = zoneWidgetData[selectedZone].find((widget: WidgetData) => widget.id === rightClickSelected); + if (selectedWidget) { + rotationStartRef.current = selectedWidget.rotation || [0, 0, 0]; + } + } + }; + + const handleMouseMove = (event: MouseEvent) => { + if (!rightClickSelected || !rightSelect) return; + const selectedZone = Object.keys(zoneWidgetData).find((zoneId: string) => + zoneWidgetData[zoneId].some((widget: WidgetData) => widget.id === rightClickSelected) + ); + if (!selectedZone) return; + + const selectedWidget = zoneWidgetData[selectedZone].find((widget: WidgetData) => widget.id === rightClickSelected); + if (!selectedWidget) return; + + const rect = gl.domElement.getBoundingClientRect(); + mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1; + mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1; + + raycaster.setFromCamera(mouse, camera); + + if (rightSelect === "Horizontal Move" && raycaster.ray.intersectPlane(plane.current, planeIntersect.current)) { + const newPosition: [number, number, number] = [ + planeIntersect.current.x, + selectedWidget.position[1], + planeIntersect.current.z + ]; + updateWidgetPosition(selectedZone, rightClickSelected, newPosition); + + } + + if (rightSelect === "Vertical Move") { + if (raycaster.ray.intersectPlane(verticalPlane.current, planeIntersect.current)) { + updateWidgetPosition(selectedZone, rightClickSelected, [ + selectedWidget.position[0], + planeIntersect.current.y, + selectedWidget.position[2] + ]); + } + } + + if (rightSelect === "RotateX") { + const deltaX = event.clientX - mouseStartRef.current.x; + const rotationSpeed = 0.03; + const newRotation: [number, number, number] = [ + rotationStartRef.current[0] + deltaX * rotationSpeed, + rotationStartRef.current[1], + rotationStartRef.current[2] + ]; + updateWidgetRotation(selectedZone, rightClickSelected, newRotation); + } + + if (rightSelect === "RotateY") { + const deltaY = event.clientY - mouseStartRef.current.y; + const rotationSpeed = 0.03; + const newRotation: [number, number, number] = [ + rotationStartRef.current[0], + rotationStartRef.current[1] + deltaY * rotationSpeed, + rotationStartRef.current[2] + ]; + updateWidgetRotation(selectedZone, rightClickSelected, newRotation); + } + if (rightSelect === "RotateZ") { + const deltaX = event.movementX; + const rotationSpeed = 0.03; + const currentRotation = selectedWidget.rotation || [0, 0, 0]; + const newRotation: [number, number, number] = [ + currentRotation[0], + currentRotation[1], + currentRotation[2] + deltaX * rotationSpeed + ]; + updateWidgetRotation(selectedZone, rightClickSelected, newRotation); + } + + }; + + const handleMouseUp = () => { + if (!rightClickSelected || !rightSelect) return; + + const selectedZone = Object.keys(zoneWidgetData).find(zoneId => + zoneWidgetData[zoneId].some(widget => widget.id === rightClickSelected) + ); + + if (!selectedZone) return; + + const selectedWidget = zoneWidgetData[selectedZone].find(widget => widget.id === rightClickSelected); + if (!selectedWidget) return; + + // Format values to 2 decimal places + const formatValues = (vals: number[]) => vals.map(val => parseFloat(val.toFixed(2))); + + if (rightSelect === "Horizontal Move" || rightSelect === "Vertical Move") { + console.log(`${rightSelect} Completed - Full Position:`, formatValues(selectedWidget.position)); + let lastPosition = formatValues(selectedWidget.position) as [number, number, number]; + + (async () => { + let response = await update3dWidget(selectedZone, organization, rightClickSelected, lastPosition); + console.log('response: ', response); + if (response) { + console.log("Widget position updated in API:", response); + } + })(); + } + else if (rightSelect.includes("Rotate")) { + const rotation = selectedWidget.rotation || [0, 0, 0]; + console.log(`${rightSelect} Completed - Full Rotation:`, formatValues(rotation)); + let lastPosition = formatValues(rotation) as [number, number, number]; + console.log('lastPosition: ', lastPosition); + + (async () => { + let response = await update3dWidgetRotation(selectedZone, organization, rightClickSelected, lastPosition); + console.log('response: ', response); + if (response) { + console.log("Widget position updated in API:", response); + } + })(); + } + + // Reset selection + setTimeout(() => { + setRightClickSelected(null); + setRightSelect(null); + }, 50); + }; + window.addEventListener("mousedown", handleMouseDown); + window.addEventListener("mousemove", handleMouseMove); + window.addEventListener("mouseup", handleMouseUp); + + return () => { + window.removeEventListener("mousedown", handleMouseDown); + window.removeEventListener("mousemove", handleMouseMove); + window.removeEventListener("mouseup", handleMouseUp); + }; + }, [rightClickSelected, rightSelect, zoneWidgetData, gl]); + + return ( + <> + {activeZoneWidgets.map(({ id, type, position, rotation = [0, 0, 0] }: WidgetData) => { + const handleRightClick = (event: React.MouseEvent, id: string) => { + event.preventDefault(); + const canvasElement = document.getElementById("real-time-vis-canvas"); + if (!canvasElement) throw new Error("Canvas element not found"); + const canvasRect = canvasElement.getBoundingClientRect(); + const relativeX = event.clientX - canvasRect.left; + const relativeY = event.clientY - canvasRect.top; + setEditWidgetOptions(true); + setRightClickSelected(id); + setTop(relativeY); + setLeft(relativeX); + }; + + switch (type) { + case "ui-Widget 1": + return ( + handleRightClick(e, id)} + /> + ); + case "ui-Widget 2": + return ( + handleRightClick(e, id)} + /> + ); + case "ui-Widget 3": + return ( + handleRightClick(e, id)} + /> + ); + case "ui-Widget 4": + return ( + handleRightClick(e, id)} + /> + ); + default: + return null; + } + })} + + ); +} \ No newline at end of file diff --git a/app/src/components/ui/componets/DroppedFloatingWidgets.tsx b/app/src/components/ui/componets/DroppedFloatingWidgets.tsx index 8cf1f86..465e8e4 100644 --- a/app/src/components/ui/componets/DroppedFloatingWidgets.tsx +++ b/app/src/components/ui/componets/DroppedFloatingWidgets.tsx @@ -112,7 +112,7 @@ const DroppedObjects: React.FC = () => { const zoneEntries = Object.entries(zones); if (zoneEntries.length === 0) return null; const [zoneName, zone] = zoneEntries[0]; - + function handleDuplicate(zoneName: string, index: number) { setOpenKebabId(null); @@ -123,14 +123,14 @@ const DroppedObjects: React.FC = () => { try { const email = localStorage.getItem("email") || ""; const organization = email?.split("@")[1]?.split(".")[0]; - - + + let deleteFloatingWidget = { floatWidgetID: id, organization: organization, zoneId: zone.zoneId } - + if (visualizationSocket) { visualizationSocket.emit("v2:viz-float:delete", deleteFloatingWidget) } @@ -143,7 +143,7 @@ const DroppedObjects: React.FC = () => { // deleteObject(zoneName, id, index); // Call the deleteObject method from the store // } } catch (error) { - + } } @@ -448,7 +448,7 @@ const DroppedObjects: React.FC = () => { // ...zone.objects[draggingIndex.index], // position: boundedPosition, // }); - + let updateFloatingWidget = { organization: organization, @@ -456,7 +456,7 @@ const DroppedObjects: React.FC = () => { ...zone.objects[draggingIndex.index], position: boundedPosition, }, - index:draggingIndex.index, + index: draggingIndex.index, zoneId: zone.zoneId } if (visualizationSocket) { @@ -464,7 +464,7 @@ const DroppedObjects: React.FC = () => { } // if (response.message === "Widget updated successfully") { - + console.log('boundedPosition: ', boundedPosition); updateObjectPosition(zoneName, draggingIndex.index, boundedPosition); // } @@ -479,7 +479,7 @@ const DroppedObjects: React.FC = () => { // animationRef.current = null; // } } catch (error) { - + } finally { // Clean up regardless of success or failure setDraggingIndex(null); @@ -509,43 +509,37 @@ const DroppedObjects: React.FC = () => { {zone.objects.map((obj, index) => (
{ @@ -559,7 +553,7 @@ const DroppedObjects: React.FC = () => { ) : obj.className === "warehouseThroughput floating" ? ( <> - + ) : obj.className === "fleetEfficiency floating" ? ( <> diff --git a/app/src/components/ui/componets/RealTimeVisulization.tsx b/app/src/components/ui/componets/RealTimeVisulization.tsx index 4b8d7c8..5a47964 100644 --- a/app/src/components/ui/componets/RealTimeVisulization.tsx +++ b/app/src/components/ui/componets/RealTimeVisulization.tsx @@ -23,11 +23,8 @@ import RenderOverlay from "../../templates/Overlay"; import ConfirmationPopup from "../../layout/confirmationPopup/ConfirmationPopup"; import DroppedObjects from "./DroppedFloatingWidgets"; import EditWidgetOption from "../menu/EditWidgetOption"; -import { - useRightClickSelected, - useRightSelected, - useEditWidgetOptionsStore, -} from "../../../store/useZone3DWidgetStore"; +import { useEditWidgetOptionsStore, useRightClickSelected, useRightSelected } from "../../../store/useZone3DWidgetStore"; + type Side = "top" | "bottom" | "left" | "right"; @@ -60,9 +57,11 @@ const RealTimeVisulization: React.FC = () => { const [droppedObjects, setDroppedObjects] = useState([]); const [zonesData, setZonesData] = useState({}); const { selectedZone, setSelectedZone } = useSelectedZoneStore(); - - const { editWidgetOptions, setEditWidgetOptions } = useEditWidgetOptionsStore(); - const { rightClickSelected, setRightClickSelected } = useRightClickSelected(); + + + const { rightSelect, setRightSelect } = useRightSelected() + const { editWidgetOptions, setEditWidgetOptions } = useEditWidgetOptionsStore() + const { rightClickSelected, setRightClickSelected } = useRightClickSelected() const [openConfirmationPopup, setOpenConfirmationPopup] = useState(false); const [floatingWidgets, setFloatingWidgets] = useState< @@ -71,7 +70,7 @@ const RealTimeVisulization: React.FC = () => { const { widgetSelect, setWidgetSelect } = useAsset3dWidget(); const { widgetSubOption, setWidgetSubOption } = useWidgetSubOption(); const { visualizationSocket } = useSocketStore(); - const { setRightSelect } = useRightSelected(); + useEffect(() => { async function GetZoneData() { @@ -100,7 +99,7 @@ const RealTimeVisulization: React.FC = () => { {} ); setZonesData(formattedData); - } catch (error) {} + } catch (error) { } } GetZoneData(); @@ -198,8 +197,27 @@ const RealTimeVisulization: React.FC = () => { ], }, })); - } catch (error) {} + } catch (error) { } }; + useEffect(() => { + const handleClickOutside = (event: MouseEvent) => { + const editWidgetOptions = document.querySelector( + ".editWidgetOptions-wrapper" + ); + if ( + editWidgetOptions && + !editWidgetOptions.contains(event.target as Node) + ) { + setRightClickSelected(null); + setRightSelect(null); + } + }; + + document.addEventListener("mousedown", handleClickOutside); + return () => { + document.removeEventListener("mousedown", handleClickOutside); + }; + }, [setRightClickSelected]); // Add this useEffect hook to your component useEffect(() => { @@ -264,14 +282,12 @@ const RealTimeVisulization: React.FC = () => { editWidgetOptions && rightClickSelected && ( diff --git a/app/src/components/ui/menu/EditWidgetOption.tsx b/app/src/components/ui/menu/EditWidgetOption.tsx index f1d6263..1c1fa2e 100644 --- a/app/src/components/ui/menu/EditWidgetOption.tsx +++ b/app/src/components/ui/menu/EditWidgetOption.tsx @@ -8,22 +8,19 @@ import { } from "../../../store/useZone3DWidgetStore"; interface EditWidgetOptionProps { - setWidgetSelect: any; options: string[]; } const EditWidgetOption: React.FC = ({ - setWidgetSelect, options, }) => { - const { top, setTop } = useTopData(); - const { left, setLeft } = useLeftData(); - const { rightSelect, setRightSelect } = useRightSelected(); + const { top } = useTopData(); + const { left } = useLeftData(); + const { setRightSelect } = useRightSelected(); const { setEditWidgetOptions } = useEditWidgetOptionsStore(); useEffect(() => { - console.log("left: ", left); - console.log("top: ", top); + }, [top, left]); return ( diff --git a/app/src/components/ui/realTimeVis/charts/BarGraphComponent.tsx b/app/src/components/ui/realTimeVis/charts/BarGraphComponent.tsx index 4876fe4..f6589a2 100644 --- a/app/src/components/ui/realTimeVis/charts/BarGraphComponent.tsx +++ b/app/src/components/ui/realTimeVis/charts/BarGraphComponent.tsx @@ -238,7 +238,7 @@ const BarGraphComponent = ({ }; useEffect(() => { - console.log("titleeeeeeeeeeeeeeeeeee",title); + },[]) // Memoize Theme Colors diff --git a/app/src/components/ui/realTimeVis/charts/DoughnutGraphComponent.tsx b/app/src/components/ui/realTimeVis/charts/DoughnutGraphComponent.tsx index 6eec49e..93c2960 100644 --- a/app/src/components/ui/realTimeVis/charts/DoughnutGraphComponent.tsx +++ b/app/src/components/ui/realTimeVis/charts/DoughnutGraphComponent.tsx @@ -51,7 +51,7 @@ const DoughnutGraphComponent = ({ }; useEffect(() => { - console.log("titleeeeeeeeeeeeeeeeeee",title); + },[]) // Memoize Theme Colors diff --git a/app/src/components/ui/realTimeVis/charts/LineGraphComponent.tsx b/app/src/components/ui/realTimeVis/charts/LineGraphComponent.tsx index bf76add..c7f7252 100644 --- a/app/src/components/ui/realTimeVis/charts/LineGraphComponent.tsx +++ b/app/src/components/ui/realTimeVis/charts/LineGraphComponent.tsx @@ -51,7 +51,7 @@ const LineGraphComponent = ({ }; useEffect(() => { - console.log("titleeeeeeeeeeeeeeeeeee",title); + },[]) // Memoize Theme Colors diff --git a/app/src/components/ui/realTimeVis/charts/PieGraphComponent.tsx b/app/src/components/ui/realTimeVis/charts/PieGraphComponent.tsx index 094b9e7..d7cc0da 100644 --- a/app/src/components/ui/realTimeVis/charts/PieGraphComponent.tsx +++ b/app/src/components/ui/realTimeVis/charts/PieGraphComponent.tsx @@ -237,7 +237,7 @@ const PieChartComponent = ({ }; useEffect(() => { - console.log("titleeeeeeeeeeeeeeeeeee",title); + },[]) // Memoize Theme Colors diff --git a/app/src/components/ui/realTimeVis/charts/PolarAreaGraphComponent.tsx b/app/src/components/ui/realTimeVis/charts/PolarAreaGraphComponent.tsx index 92581c0..fb87080 100644 --- a/app/src/components/ui/realTimeVis/charts/PolarAreaGraphComponent.tsx +++ b/app/src/components/ui/realTimeVis/charts/PolarAreaGraphComponent.tsx @@ -51,7 +51,7 @@ const PolarAreaGraphComponent = ({ }; useEffect(() => { - console.log("titleeeeeeeeeeeeeeeeeee",title); + },[]) // Memoize Theme Colors diff --git a/app/src/services/realTimeVisulization/zoneData/add3dWidget.ts b/app/src/services/realTimeVisulization/zoneData/add3dWidget.ts index 82562b7..401a5bf 100644 --- a/app/src/services/realTimeVisulization/zoneData/add3dWidget.ts +++ b/app/src/services/realTimeVisulization/zoneData/add3dWidget.ts @@ -5,9 +5,6 @@ export const adding3dWidgets = async ( organization: string, widget: {} ) => { - console.log('widget: ', widget); - console.log('organization: ', organization); - console.log('zoneId: ', zoneId); try { const response = await fetch( `${url_Backend_dwinzo}/api/v2/3dwidget/save`, diff --git a/app/src/services/realTimeVisulization/zoneData/delete3dWidget.ts b/app/src/services/realTimeVisulization/zoneData/delete3dWidget.ts new file mode 100644 index 0000000..fe868f1 --- /dev/null +++ b/app/src/services/realTimeVisulization/zoneData/delete3dWidget.ts @@ -0,0 +1,38 @@ +let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`; +// let url_Backend_dwinzo = `http://192.168.0.102:5000`; + +export const delete3dWidgetApi = async ( + zoneId: string, + organization: string, + id: string +) => { + console.log("zoneId: ", zoneId); + console.log("organization: ", organization); + console.log("id: ", id); + + try { + const response = await fetch( + `${url_Backend_dwinzo}/api/v2/widget3D/delete`, + { + method: "PATCH", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ organization, zoneId, id }), + } + ); + + if (!response.ok) { + throw new Error("Failed to delete floating widget in the zone"); + } + + const result = await response.json(); + return result; + } catch (error) { + if (error instanceof Error) { + throw new Error(error.message); + } else { + throw new Error("An unknown error occurred"); + } + } +}; diff --git a/app/src/services/realTimeVisulization/zoneData/update3dWidget.ts b/app/src/services/realTimeVisulization/zoneData/update3dWidget.ts new file mode 100644 index 0000000..553fc68 --- /dev/null +++ b/app/src/services/realTimeVisulization/zoneData/update3dWidget.ts @@ -0,0 +1,81 @@ +let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`; +// let url_Backend_dwinzo = `http://192.168.0.102:5000`; +export const update3dWidget = async ( + zoneId: string, + organization: string, + id: string, + position?: [number, number, number] +) => { + console.log("organization: ", organization); + console.log("zoneId: ", zoneId); + try { + const response = await fetch( + `${url_Backend_dwinzo}/api/v2/modifyPR/widget3D`, + { + method: "PATCH", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + organization, + zoneId, + id, + position, + }), + } + ); + + if (!response.ok) { + throw new Error("Failed to add 3dwidget in the zone"); + } + + const result = await response.json(); + return result; + } catch (error) { + if (error instanceof Error) { + throw new Error(error.message); + } else { + throw new Error("An unknown error occurred"); + } + } +}; + +export const update3dWidgetRotation = async ( + zoneId: string, + organization: string, + id: string, + rotation?: [number, number, number] +) => { + console.log("organization: ", organization); + console.log("zoneId: ", zoneId); + try { + const response = await fetch( + `${url_Backend_dwinzo}/api/v2/modifyPR/widget3D`, + { + method: "PATCH", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + organization, + zoneId, + id, + rotation, + }), + } + ); + + if (!response.ok) { + throw new Error("Failed to add 3dwidget in the zone"); + } + + const result = await response.json(); + return result; + } catch (error) { + if (error instanceof Error) { + throw new Error(error.message); + } else { + throw new Error("An unknown error occurred"); + } + } +}; diff --git a/app/src/store/useZone3DWidgetStore.ts b/app/src/store/useZone3DWidgetStore.ts index 425674f..7dcaaeb 100644 --- a/app/src/store/useZone3DWidgetStore.ts +++ b/app/src/store/useZone3DWidgetStore.ts @@ -97,7 +97,6 @@ export const useRightSelected = create((set) => ({ setRightSelect: (x) => set({ rightSelect: x }), })); - interface EditWidgetOptionsStore { editWidgetOptions: boolean; setEditWidgetOptions: (value: boolean) => void;