From 8fc4453cee904607f07a975bace8ec98bbe170c1 Mon Sep 17 00:00:00 2001 From: Vishnu Date: Mon, 31 Mar 2025 11:11:44 +0530 Subject: [PATCH 01/11] Refactor input styles, implement 3D toggle state management, and enhance FileMenu with dropdown options --- app/src/components/ui/FileMenu.tsx | 14 +- app/src/components/ui/Tools.tsx | 108 +++++++----- app/src/components/ui/menu/menu.tsx | 209 +++++++---------------- app/src/pages/Project.tsx | 6 +- app/src/store/useModuleStore.ts | 15 +- app/src/styles/components/input.scss | 12 +- app/src/styles/components/menu/menu.scss | 74 +++++--- 7 files changed, 218 insertions(+), 220 deletions(-) diff --git a/app/src/components/ui/FileMenu.tsx b/app/src/components/ui/FileMenu.tsx index 475d01f..a7c876d 100644 --- a/app/src/components/ui/FileMenu.tsx +++ b/app/src/components/ui/FileMenu.tsx @@ -1,12 +1,24 @@ -import React from "react"; +import React, { useState } from "react"; import RenameInput from "./inputs/RenameInput"; +import { ArrowIcon } from "../icons/ExportCommonIcons"; +import MenuBar from "./menu/menu"; const FileMenu: React.FC = () => { + const [openMenu, setOpenMenu] = useState(false); return (
+
{ + setOpenMenu(!openMenu); + }} + > + + {openMenu && } +
); }; diff --git a/app/src/components/ui/Tools.tsx b/app/src/components/ui/Tools.tsx index c86164c..e03484d 100644 --- a/app/src/components/ui/Tools.tsx +++ b/app/src/components/ui/Tools.tsx @@ -14,7 +14,7 @@ import { ZoneIcon, } from "../icons/ExportToolsIcons"; import { ArrowIcon, TickIcon } from "../icons/ExportCommonIcons"; -import useModuleStore from "../../store/useModuleStore"; +import useModuleStore, { useThreeDStore } from "../../store/useModuleStore"; import { handleSaveTemplate } from "../../modules/visualization/handleSaveTemplate"; import { usePlayButtonStore } from "../../store/usePlayButtonStore"; import useTemplateStore from "../../store/useTemplateStore"; @@ -36,8 +36,8 @@ import useToggleStore from "../../store/useUIToggleStore"; const Tools: React.FC = () => { const { templates } = useTemplateStore(); const [activeSubTool, setActiveSubTool] = useState("cursor"); - const [toggleThreeD, setToggleThreeD] = useState(true); - const { toggleUI, setToggleUI } = useToggleStore(); + const { toggleThreeD, setToggleThreeD } = useThreeDStore(); + const { setToggleUI } = useToggleStore(); const dropdownRef = useRef(null); const [openDrop, setOpenDrop] = useState(false); @@ -62,7 +62,11 @@ const Tools: React.FC = () => { // Reset activeTool whenever activeModule changes useEffect(() => { - setToggleUI(localStorage.getItem('navBarUi') ? localStorage.getItem('navBarUi') === 'true' : true) + setToggleUI( + localStorage.getItem("navBarUi") + ? localStorage.getItem("navBarUi") === "true" + : true + ); }, []); useEffect(() => { @@ -80,7 +84,11 @@ const Tools: React.FC = () => { } else { setToggleView(false); } - setToggleUI(localStorage.getItem('navBarUi') ? localStorage.getItem('navBarUi') === 'true' : true) + setToggleUI( + localStorage.getItem("navBarUi") + ? localStorage.getItem("navBarUi") === "true" + : true + ); setToggleThreeD(!toggleThreeD); setActiveSubTool("cursor"); setActiveTool("cursor"); @@ -202,8 +210,9 @@ const Tools: React.FC = () => {
{activeSubTool == "cursor" && (
{ setActiveTool("cursor"); }} @@ -213,8 +222,9 @@ const Tools: React.FC = () => { )} {activeSubTool == "free-hand" && (
{ setActiveTool("free-hand"); }} @@ -224,8 +234,9 @@ const Tools: React.FC = () => { )} {activeSubTool == "delete" && (
{ setActiveTool("delete"); }} @@ -297,8 +308,9 @@ const Tools: React.FC = () => {
{ setActiveTool("draw-wall"); }} @@ -307,8 +319,9 @@ const Tools: React.FC = () => {
{ setActiveTool("draw-zone"); }} @@ -317,8 +330,9 @@ const Tools: React.FC = () => {
{ setActiveTool("draw-aisle"); }} @@ -327,8 +341,9 @@ const Tools: React.FC = () => {
{ setActiveTool("draw-floor"); }} @@ -344,8 +359,9 @@ const Tools: React.FC = () => {
{ setActiveTool("measure"); }} @@ -361,8 +377,9 @@ const Tools: React.FC = () => {
{ setActiveTool("pen"); }} @@ -394,8 +411,9 @@ const Tools: React.FC = () => {
{ setActiveTool("comment"); }} @@ -404,8 +422,9 @@ const Tools: React.FC = () => {
{toggleThreeD && (
{ setIsPlaying(!isPlaying); }} @@ -414,19 +433,28 @@ const Tools: React.FC = () => {
)}
-
-
-
- 2d -
-
- 3d -
-
+ {activeModule === "builder" && ( + <> +
+
+
+ 2d +
+
+ 3d +
+
+ + )}
) : ( diff --git a/app/src/components/ui/menu/menu.tsx b/app/src/components/ui/menu/menu.tsx index b374237..b4ab78e 100644 --- a/app/src/components/ui/menu/menu.tsx +++ b/app/src/components/ui/menu/menu.tsx @@ -1,6 +1,11 @@ import React, { useState } from "react"; +import { ArrowIcon } from "../../icons/ExportCommonIcons"; -const MenuBar = () => { +interface MenuBarProps { + setOpenMenu: (isOpen: boolean) => void; // Function to update menu state +} + +const MenuBar: React.FC = ({ setOpenMenu }) => { const [activeMenu, setActiveMenu] = useState(null); const [activeSubMenu, setActiveSubMenu] = useState(null); @@ -18,7 +23,12 @@ const MenuBar = () => { }; return ( -
+
{ + setOpenMenu(false); + }} + > {/* Top-level menu buttons */}
{/* File Menu */} @@ -32,7 +42,9 @@ const MenuBar = () => { >
File - + + +
{/* File Dropdown */} @@ -44,10 +56,7 @@ const MenuBar = () => { onClick={() => toggleSelection("New File")} >
- - {selectedItems["New File"] && "✓ "} - New File - + New File
Ctrl + N
@@ -60,10 +69,7 @@ const MenuBar = () => { onClick={() => toggleSelection("Open Local File")} >
- - {selectedItems["Open Local File"] && "✓ "} - Open Local File - + Open Local File
Ctrl + O
@@ -76,10 +82,7 @@ const MenuBar = () => { onClick={() => toggleSelection("Save Version")} >
- - {selectedItems["Save Version"] && "✓ "} - Save Version - + Save Version
@@ -90,10 +93,7 @@ const MenuBar = () => { onClick={() => toggleSelection("Make a Copy")} >
- - {selectedItems["Make a Copy"] && "✓ "} - Make a Copy - + Make a Copy
@@ -103,10 +103,7 @@ const MenuBar = () => { onClick={() => toggleSelection("Share")} >
- - {selectedItems["Share"] && "✓ "} - Share - + Share
@@ -116,10 +113,7 @@ const MenuBar = () => { onClick={() => toggleSelection("Rename")} >
- - {selectedItems["Rename"] && "✓ "} - Rename - + Rename
@@ -130,10 +124,7 @@ const MenuBar = () => { onClick={() => toggleSelection("Import")} >
- - {selectedItems["Import"] && "✓ "} - Import - + Import
@@ -143,10 +134,7 @@ const MenuBar = () => { onClick={() => toggleSelection("Close File")} >
- - {selectedItems["Close File"] && "✓ "} - Close File - + Close File
@@ -164,7 +152,9 @@ const MenuBar = () => { >
Edit - + + +
{/* Edit Dropdown */} @@ -176,10 +166,7 @@ const MenuBar = () => { onClick={() => toggleSelection("Undo")} >
- - {selectedItems["Undo"] && "✓ "} - Undo - + Undo
Ctrl + Z
@@ -192,10 +179,7 @@ const MenuBar = () => { onClick={() => toggleSelection("Redo")} >
- - {selectedItems["Redo"] && "✓ "} - Redo - + Redo
Ctrl + Shift + Z
@@ -209,10 +193,7 @@ const MenuBar = () => { onClick={() => toggleSelection("Undo History")} >
- - {selectedItems["Undo History"] && "✓ "} - Undo History - + Undo History
@@ -222,10 +203,7 @@ const MenuBar = () => { onClick={() => toggleSelection("Redo History")} >
- - {selectedItems["Redo History"] && "✓ "} - Redo History - + Redo History
@@ -236,10 +214,7 @@ const MenuBar = () => { onClick={() => toggleSelection("Find")} >
- - {selectedItems["Find"] && "✓ "} - Find - + Find
Ctrl + F
@@ -252,10 +227,7 @@ const MenuBar = () => { onClick={() => toggleSelection("Delete")} >
- - {selectedItems["Delete"] && "✓ "} - Delete - + Delete
@@ -265,10 +237,7 @@ const MenuBar = () => { onClick={() => toggleSelection("Select by...")} >
- - {selectedItems["Select by..."] && "✓ "} - Select by... - + Select by...
@@ -278,10 +247,7 @@ const MenuBar = () => { onClick={() => toggleSelection("Keymap")} >
- - {selectedItems["Keymap"] && "✓ "} - Keymap - + Keymap
@@ -299,7 +265,9 @@ const MenuBar = () => { >
View - + + +
{/* View Dropdown */} @@ -311,10 +279,7 @@ const MenuBar = () => { onClick={() => toggleSelection("Grid")} >
- - {selectedItems["Grid"] && "✓ "} - Grid - + Grid
@@ -325,11 +290,10 @@ const MenuBar = () => { onMouseLeave={() => setActiveSubMenu(null)} >
- - {selectedItems["Gizmo"] && "✓ "} - Gizmo + Gizmo + + -
@@ -346,16 +310,14 @@ const MenuBar = () => { Visibility
+
{/* Cube view */}
toggleSelection("Cube view")} > - - {selectedItems["Cube view"] && "✓ "} - Cube view - + Cube view
{/* Sphere view */} @@ -363,21 +325,7 @@ const MenuBar = () => { className="submenu-item" onClick={() => toggleSelection("Sphere view")} > - - {selectedItems["Sphere view"] && "✓ "} - Sphere view - -
- - {/* Custom settings */} -
toggleSelection("Custom settings")} - > - - {selectedItems["Custom settings"] && "✓ "} - Custom settings - + Sphere view
)} @@ -389,10 +337,7 @@ const MenuBar = () => { onClick={() => toggleSelection("Zoom")} >
- - {selectedItems["Zoom"] && "✓ "} - Zoom - + Zoom
@@ -402,10 +347,7 @@ const MenuBar = () => { onClick={() => toggleSelection("Full Screen")} >
- - {selectedItems["Full Screen"] && "✓ "} - Full Screen - + Full Screen
F11
@@ -424,11 +366,7 @@ const MenuBar = () => { setActiveSubMenu(null); }} > -
- Version history -
- - +
Version history
{/* Export As Menu */} @@ -440,14 +378,11 @@ const MenuBar = () => { setActiveSubMenu(null); }} > -
- Export as... -
- +
Export as...
{/* Apps Menu */} -
setActiveMenu("Apps")} onMouseLeave={() => { @@ -457,68 +392,64 @@ const MenuBar = () => { >
Apps - + + +
- {/* Apps Dropdown */} {activeMenu === "Apps" && (
- {/* New App */}
toggleSelection("New App")} >
- {selectedItems["New App"] && "✓ "} + New App
-
- {/* Work-flow Monitor */}
toggleSelection("Work-flow Monitor")} >
- {selectedItems["Work-flow Monitor"] && "✓ "} + Work-flow Monitor
- {/* Temperature Visualizer */}
toggleSelection("Temperature Visualizer")} >
- {selectedItems["Temperature Visualizer"] && "✓ "} + Temperature Visualizer
- {/* View all */}
toggleSelection("View all")} >
- {selectedItems["View all"] && "✓ "} + View all
)} -
+ */} {/* Help Menu */}
{ >
Help - + + +
{/* Help Dropdown */} @@ -543,10 +476,7 @@ const MenuBar = () => { onClick={() => toggleSelection("Shortcuts")} >
- - {selectedItems["Shortcuts"] && "✓ "} - Shortcuts - + Shortcuts
Ctrl + Shift + ?
@@ -559,10 +489,7 @@ const MenuBar = () => { onClick={() => toggleSelection("Manual")} >
- - {selectedItems["Manual"] && "✓ "} - Manual - + Manual
@@ -572,10 +499,7 @@ const MenuBar = () => { onClick={() => toggleSelection("Video Tutorials")} >
- - {selectedItems["Video Tutorials"] && "✓ "} - Video Tutorials - + Video Tutorials
@@ -585,10 +509,7 @@ const MenuBar = () => { onClick={() => toggleSelection("Report a bug")} >
- - {selectedItems["Report a bug"] && "✓ "} - Report a bug - + Report a bug
@@ -600,5 +521,3 @@ const MenuBar = () => { }; export default MenuBar; - - diff --git a/app/src/pages/Project.tsx b/app/src/pages/Project.tsx index a7800e1..41f04a7 100644 --- a/app/src/pages/Project.tsx +++ b/app/src/pages/Project.tsx @@ -2,7 +2,7 @@ import React, { useEffect } from "react"; import ModuleToggle from "../components/ui/ModuleToggle"; import SideBarLeft from "../components/layout/sidebarLeft/SideBarLeft"; import SideBarRight from "../components/layout/sidebarRight/SideBarRight"; -import useModuleStore from "../store/useModuleStore"; +import useModuleStore, { useThreeDStore } from "../store/useModuleStore"; import RealTimeVisulization from "../components/ui/componets/RealTimeVisulization"; import Tools from "../components/ui/Tools"; // import Scene from "../modules/scene/scene"; @@ -49,14 +49,14 @@ const Project: React.FC = () => { } }, []); const { isPlaying } = usePlayButtonStore(); + const { toggleThreeD } = useThreeDStore(); return (
{loadingProgress && } {!isPlaying && ( <> - - + {toggleThreeD && } diff --git a/app/src/store/useModuleStore.ts b/app/src/store/useModuleStore.ts index 6373af5..1012792 100644 --- a/app/src/store/useModuleStore.ts +++ b/app/src/store/useModuleStore.ts @@ -23,4 +23,17 @@ const useSubModuleStore = create((set) => ({ setSubModule: (subModule) => set({ subModule }), // Update subModule state })); -export { useSubModuleStore }; \ No newline at end of file +export { useSubModuleStore }; + +interface ThreeDState { + toggleThreeD: boolean; + setToggleThreeD: (value: boolean) => void; +} + +// Create the Zustand store +const useThreeDStore = create((set) => ({ + toggleThreeD: true, // Initial state + setToggleThreeD: (value) => set({ toggleThreeD: value }), // Action to update the state +})); + +export { useThreeDStore }; \ No newline at end of file diff --git a/app/src/styles/components/input.scss b/app/src/styles/components/input.scss index f60aca6..e5dd2b4 100644 --- a/app/src/styles/components/input.scss +++ b/app/src/styles/components/input.scss @@ -155,16 +155,6 @@ } } -.project-dropdowm-container { - position: relative; - height: 32px; - - .project-name { - line-height: 32px; - height: 100%; - } -} - .regularDropdown-container { width: 100%; min-width: 80px; @@ -655,4 +645,4 @@ input { .multi-email-invite-input.active { border: 1px solid var(--accent-color); } -} \ No newline at end of file +} diff --git a/app/src/styles/components/menu/menu.scss b/app/src/styles/components/menu/menu.scss index a4348a3..8635b0d 100644 --- a/app/src/styles/components/menu/menu.scss +++ b/app/src/styles/components/menu/menu.scss @@ -1,25 +1,58 @@ +@use "../../abstracts/variables" as *; +@use "../../abstracts/mixins" as *; + +.project-dropdowm-container { + display: flex; + align-items: center; + gap: 2px; + position: relative; + height: 32px; + .project-name { + line-height: 32px; + height: 100%; + } + .more-options-button { + @include flex-center; + border-radius: #{$border-radius-small}; + height: 28px; + position: relative; + &:hover { + background: var(--highlight-accent-color); + path { + fill: var(--accent-color); + } + } + } + .more-options-button.active { + background: var(--highlight-accent-color); + path { + fill: var(--accent-color); + } + } +} + .menu-bar { position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); + top: 32px; + left: 0; z-index: 5; background-color: var(--background-color); color: var(--text-color); box-shadow: var(--box-shadow-light); border-radius: 8px; + border: 1px solid var(--border-color); .menu-buttons { display: flex; flex-direction: column; height: 100%; - padding: 8px 4px; + padding: 4px; min-width: 178px; .menu-button-container { position: relative; height: 100%; - padding: 8px; - + padding: 4px 8px 4px 12px; + border-radius: #{$border-radius-small}; .menu-button { width: 100%; cursor: pointer; @@ -32,7 +65,7 @@ .dropdown-icon { margin-left: 5px; font-size: var(--font-size-small); - color: #666666; + rotate: -90deg; } } @@ -46,20 +79,21 @@ box-shadow: var(--box-shadow-light); border: 1px solid var(--background-color); z-index: 100; - padding: 5px 0; - + padding: 4px; .menu-item-container { position: relative; - .menu-item { + padding: 4px 8px 4px 12px; + border-radius: #{$border-radius-small}; display: flex; justify-content: space-between; align-items: center; - padding: 8px 20px; cursor: pointer; white-space: nowrap; color: var(--text-color); - + .dropdown-icon { + rotate: -90deg; + } &:hover { background-color: var(--highlight-accent-color); color: var(--highlight-accent-color); @@ -92,19 +126,20 @@ box-shadow: var(--box-shadow-light); border: 1px solid var(--background-color); z-index: 101; - + padding: 4px; .submenu-item { - padding: 8px 20px; - cursor: pointer; + padding: 4px 8px 4px 12px; + border-radius: #{$border-radius-small}; display: flex; justify-content: space-between; + align-items: center; + cursor: pointer; + white-space: nowrap; color: var(--text-color); - &:hover { - background-color: var(--background-color-gray); + background-color: var(--highlight-accent-color); color: var(--highlight-accent-color); } - .shortcut { color: var(--text-color); } @@ -122,6 +157,7 @@ .split { width: 100%; height: 1px; - background-color: #e0dfff; + background-color: var(--highlight-accent-color); + margin: 2px 0; } } From 6e4c8282c5dd1ee2e7dd38034d298e6fd85c6bfb Mon Sep 17 00:00:00 2001 From: Jerald-Golden-B Date: Mon, 31 Mar 2025 14:28:24 +0530 Subject: [PATCH 02/11] Refactor AssetProperties layout, enhance PositionInput component with optional properties, and implement new asset event fetching logic --- .../customInput/PositionInputs.tsx | 22 +++- .../mechanics/VehicleMechanics.tsx | 72 ++++++++--- .../properties/AssetProperties.tsx | 2 - .../controls/selection/selectionControls.tsx | 36 ++++-- .../simulation/behaviour/behaviour.tsx | 8 +- .../modules/simulation/path/pathConnector.tsx | 115 +++++------------- .../modules/simulation/path/pathCreation.tsx | 102 ++++++++++++++-- app/src/modules/simulation/simulation.tsx | 6 +- .../services/simulation/getAssetEventType.ts | 26 ++++ app/src/store/store.ts | 21 ++++ app/src/types/world/worldTypes.d.ts | 2 +- 11 files changed, 288 insertions(+), 124 deletions(-) create mode 100644 app/src/services/simulation/getAssetEventType.ts diff --git a/app/src/components/layout/sidebarRight/customInput/PositionInputs.tsx b/app/src/components/layout/sidebarRight/customInput/PositionInputs.tsx index 881e225..01b4ab3 100644 --- a/app/src/components/layout/sidebarRight/customInput/PositionInputs.tsx +++ b/app/src/components/layout/sidebarRight/customInput/PositionInputs.tsx @@ -1,23 +1,32 @@ import React from "react"; +import { EyeDroperIcon } from "../../../icons/ExportCommonIcons"; interface PositionInputProps { + label?: string; // Optional label for the input onChange: (value: string) => void; // Callback for value change placeholder?: string; // Optional placeholder type?: string; // Input type (e.g., text, number, email) value1?: number; value2?: number; + disabled?: boolean; // Optional disabled property + isEyedrop?: boolean; // Optional eyedrop property + handleEyeDropClick?: () => void; // Optional function for eye drop click } const PositionInput: React.FC = ({ onChange, + label = "Position", // Default label placeholder = "Enter value", // Default placeholder type = "number", // Default type value1 = "number", value2 = "number", + disabled = false, // Default disabled value + isEyedrop = false, // Default isEyedrop value + handleEyeDropClick = () => { }, // Default function for eye drop click }) => { return (
-
Position
+
{label}
X :
@@ -26,7 +35,8 @@ const PositionInput: React.FC = ({ type={type} onChange={(e) => onChange(e.target.value)} placeholder={placeholder} - value={value2} + value={value1} + disabled={disabled} // Apply disabled prop />
@@ -36,10 +46,16 @@ const PositionInput: React.FC = ({ type={type} onChange={(e) => onChange(e.target.value)} placeholder={placeholder} - value={value1} + value={value2} + disabled={disabled} // Apply disabled prop />
+ {isEyedrop && ( +
+ +
+ )}
); }; diff --git a/app/src/components/layout/sidebarRight/mechanics/VehicleMechanics.tsx b/app/src/components/layout/sidebarRight/mechanics/VehicleMechanics.tsx index c611a13..bf0b112 100644 --- a/app/src/components/layout/sidebarRight/mechanics/VehicleMechanics.tsx +++ b/app/src/components/layout/sidebarRight/mechanics/VehicleMechanics.tsx @@ -1,13 +1,16 @@ import React, { useRef, useMemo } from "react"; import { InfoIcon } from "../../../icons/ExportCommonIcons"; import InputWithDropDown from "../../../ui/inputs/InputWithDropDown"; -import { useSelectedActionSphere, useSimulationPaths } from "../../../../store/store"; +import { useEditingPoint, useEyeDropMode, usePreviewPosition, useSelectedActionSphere, useSimulationPaths } from "../../../../store/store"; import * as Types from '../../../../types/world/worldTypes'; -import LabledDropdown from "../../../ui/inputs/LabledDropdown"; +import PositionInput from "../customInput/PositionInputs"; const VehicleMechanics: React.FC = () => { const { selectedActionSphere } = useSelectedActionSphere(); const { simulationPaths, setSimulationPaths } = useSimulationPaths(); + const { eyeDropMode, setEyeDropMode } = useEyeDropMode(); + const { editingPoint, setEditingPoint } = useEditingPoint(); + const { previewPosition, setPreviewPosition } = usePreviewPosition(); const propertiesContainerRef = useRef(null); @@ -59,12 +62,10 @@ const VehicleMechanics: React.FC = () => { setSimulationPaths(updatedPaths); }, [selectedActionSphere?.point?.uuid, simulationPaths, setSimulationPaths]); - const handleStartPointChange = React.useCallback((uuid: string) => { - handleActionUpdate({ start: uuid }); + const handleStartPointChange = React.useCallback((position: { x: number, y: number }) => { }, [handleActionUpdate]); - const handleEndPointChange = React.useCallback((uuid: string) => { - handleActionUpdate({ end: uuid }); + const handleEndPointChange = React.useCallback((position: { x: number, y: number }) => { }, [handleActionUpdate]); const handleHitCountChange = React.useCallback((hitCount: number) => { @@ -94,6 +95,16 @@ const VehicleMechanics: React.FC = () => { setSimulationPaths(updatedPaths); }, [selectedActionSphere?.point?.uuid, simulationPaths, setSimulationPaths]); + const handleStartEyeDropClick = () => { + setEditingPoint('start'); + setEyeDropMode(true); + }; + + const handleEndEyeDropClick = () => { + setEditingPoint('end'); + setEyeDropMode(true); + }; + return (
@@ -106,20 +117,49 @@ const VehicleMechanics: React.FC = () => { {selectedPoint && ( <> - { }} + disabled={true} + value1={ + editingPoint === 'start' && previewPosition + ? parseFloat(previewPosition.x.toFixed(4)) + : selectedPoint.actions.start && 'x' in selectedPoint.actions.start + ? parseFloat(selectedPoint.actions.start.x.toFixed(4)) + : 0 + } + value2={ + editingPoint === 'start' && previewPosition + ? parseFloat(previewPosition.y.toFixed(4)) + : selectedPoint.actions.start && 'y' in selectedPoint.actions.start + ? parseFloat(selectedPoint.actions.start.y.toFixed(4)) + : 0 + } + + isEyedrop={true} + handleEyeDropClick={handleStartEyeDropClick} /> - { }} + disabled={true} + value1={ + editingPoint === 'end' && previewPosition + ? parseFloat(previewPosition.x.toFixed(4)) + : selectedPoint.actions.end && 'x' in selectedPoint.actions.end + ? parseFloat(selectedPoint.actions.end.x.toFixed(4)) + : 0 + } + value2={ + editingPoint === 'end' && previewPosition + ? parseFloat(previewPosition.y.toFixed(4)) + : selectedPoint.actions.end && 'y' in selectedPoint.actions.end + ? parseFloat(selectedPoint.actions.end.y.toFixed(4)) + : 0 + } + isEyedrop={true} + handleEyeDropClick={handleEndEyeDropClick} /> { {/* Name */}
{selectedFloorItem.userData.name}
-
- {}} value1={xValue.toFixed(5)} diff --git a/app/src/modules/scene/controls/selection/selectionControls.tsx b/app/src/modules/scene/controls/selection/selectionControls.tsx index c9038ab..9d5f7ea 100644 --- a/app/src/modules/scene/controls/selection/selectionControls.tsx +++ b/app/src/modules/scene/controls/selection/selectionControls.tsx @@ -42,6 +42,8 @@ const SelectionControls: React.FC = () => { itemsGroupRef.current = itemsGroup; let isSelecting = false; + let isRightClick = false; + let rightClickMoved = false; let isCtrlSelecting = false; const helper = new SelectionHelper(gl); @@ -52,16 +54,23 @@ const SelectionControls: React.FC = () => { } const onPointerDown = (event: PointerEvent) => { - if (event.button !== 0) return - isSelecting = false; - isCtrlSelecting = event.ctrlKey; - if (event.ctrlKey && duplicatedObjects.length === 0) { - if (controls) (controls as any).enabled = false; - selectionBox.startPoint.set(pointer.x, pointer.y, 0); + if (event.button === 2) { + isRightClick = true; + rightClickMoved = false; + } else if (event.button === 0) { + isSelecting = false; + isCtrlSelecting = event.ctrlKey; + if (event.ctrlKey && duplicatedObjects.length === 0) { + if (controls) (controls as any).enabled = false; + selectionBox.startPoint.set(pointer.x, pointer.y, 0); + } } }; const onPointerMove = (event: PointerEvent) => { + if (isRightClick) { + rightClickMoved = true; + } isSelecting = true; if (helper.isDown && event.ctrlKey && duplicatedObjects.length === 0 && isCtrlSelecting) { selectionBox.endPoint.set(pointer.x, pointer.y, 0); @@ -69,6 +78,14 @@ const SelectionControls: React.FC = () => { }; const onPointerUp = (event: PointerEvent) => { + if (event.button === 2) { + isRightClick = false; + if (!rightClickMoved) { + clearSelection(); + } + return; + } + if (isSelecting && isCtrlSelecting) { isCtrlSelecting = false; isSelecting = false; @@ -94,10 +111,13 @@ const SelectionControls: React.FC = () => { } }; + const onContextMenu = (event: MouseEvent) => { event.preventDefault(); - clearSelection(); - } + if (!rightClickMoved) { + clearSelection(); + } + }; if (!toggleView && activeModule === "builder") { helper.enabled = true; diff --git a/app/src/modules/simulation/behaviour/behaviour.tsx b/app/src/modules/simulation/behaviour/behaviour.tsx index c1281b5..afb4ae9 100644 --- a/app/src/modules/simulation/behaviour/behaviour.tsx +++ b/app/src/modules/simulation/behaviour/behaviour.tsx @@ -2,16 +2,22 @@ import { useFloorItems, useSimulationPaths } from '../../../store/store'; import * as THREE from 'three'; import * as Types from '../../../types/world/worldTypes'; import { useEffect } from 'react'; +import { getAssetEventType } from '../../../services/simulation/getAssetEventType'; function Behaviour() { const { setSimulationPaths } = useSimulationPaths(); const { floorItems } = useFloorItems(); useEffect(() => { + const email = localStorage.getItem('email') + const organization = (email!.split("@")[1]).split(".")[0]; const newPaths: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema)[] = []; floorItems.forEach((item: Types.FloorItemType) => { if (item.modelfileID === "672a090f80d91ac979f4d0bd") { + // getAssetEventType(item.modelfileID, organization).then((res) => { + // console.log('res: ', res); + // }); const point1Position = new THREE.Vector3(0, 0.85, 2.2); const middlePointPosition = new THREE.Vector3(0, 0.85, 0); const point2Position = new THREE.Vector3(0, 0.85, -2.2); @@ -67,7 +73,7 @@ function Behaviour() { point: { uuid: pointUUID, position: [pointPosition.x, pointPosition.y, pointPosition.z], - actions: { uuid: THREE.MathUtils.generateUUID(), name: 'Action 1', type: 'Start', start: '', hitCount: 1, end: '', buffer: 0 }, + actions: { uuid: THREE.MathUtils.generateUUID(), name: 'Action 1', type: 'Start', start: {}, hitCount: 1, end: {}, buffer: 0 }, connections: { source: { pathUUID: item.modeluuid, pointUUID: pointUUID }, targets: [] }, speed: 2, }, diff --git a/app/src/modules/simulation/path/pathConnector.tsx b/app/src/modules/simulation/path/pathConnector.tsx index 0888966..7352932 100644 --- a/app/src/modules/simulation/path/pathConnector.tsx +++ b/app/src/modules/simulation/path/pathConnector.tsx @@ -96,24 +96,16 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec }; const existingTargets = path.point.connections.targets || []; - // Check if we're trying to add a connection to a Conveyor + // Check if target is a Conveyor const toPath = simulationPaths.find(p => p.modeluuid === toPathUUID); - const isConnectingToConveyor = toPath?.type === 'Conveyor'; - - // Count existing connections - if (existingTargets.length >= 2) { - console.log("Vehicle can have maximum 2 connections"); + if (toPath?.type !== 'Conveyor') { + console.log("Vehicle can only connect to Conveyors"); return path; } - // Check if we already have a Conveyor connection and trying to add another - const hasConveyorConnection = existingTargets.some(target => { - const targetPath = simulationPaths.find(p => p.modeluuid === target.pathUUID); - return targetPath?.type === 'Conveyor'; - }); - - if (hasConveyorConnection && isConnectingToConveyor) { - console.log("Vehicle can only have one connection to a Conveyor"); + // Check if already has a connection + if (existingTargets.length >= 1) { + console.log("Vehicle can have only one connection"); return path; } @@ -141,24 +133,16 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec }; const existingTargets = path.point.connections.targets || []; - // Check if we're receiving a connection from a Conveyor + // Check if source is a Conveyor const fromPath = simulationPaths.find(p => p.modeluuid === fromPathUUID); - const isConnectingFromConveyor = fromPath?.type === 'Conveyor'; - - // Count existing connections - if (existingTargets.length >= 2) { - console.log("Vehicle can have maximum 2 connections"); + if (fromPath?.type !== 'Conveyor') { + console.log("Vehicle can only connect to Conveyors"); return path; } - // Check if we already have a Conveyor connection and trying to add another - const hasConveyorConnection = existingTargets.some(target => { - const targetPath = simulationPaths.find(p => p.modeluuid === target.pathUUID); - return targetPath?.type === 'Conveyor'; - }); - - if (hasConveyorConnection && isConnectingFromConveyor) { - console.log("Vehicle can only have one connection to a Conveyor"); + // Check if already has a connection + if (existingTargets.length >= 1) { + console.log("Vehicle can have only one connection"); return path; } @@ -212,6 +196,7 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec drag = true; } }; + const onContextMenu = (evt: MouseEvent) => { evt.preventDefault(); if (drag || evt.button === 0) return; @@ -282,7 +267,16 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec return; } - // For Vehicles, skip the "already connected" check since they can have multiple connections + // For Vehicles, check if they're already connected to anything + if (intersected.userData.path.type === 'Vehicle') { + const vehicleConnections = intersected.userData.path.point.connections.targets.length; + if (vehicleConnections >= 1) { + console.log("Vehicle can only have one connection"); + return; + } + } + + // For non-Vehicle paths, check if already connected if (intersected.userData.path.type !== 'Vehicle') { const isAlreadyConnected = simulationPaths.some(path => { if (path.type === 'Conveyor') { @@ -300,48 +294,14 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec } } - // Check vehicle connection limits - const checkVehicleConnections = (pathUUID: string) => { - const path = simulationPaths.find(p => p.modeluuid === pathUUID); - if (path?.type === 'Vehicle') { - return path.point.connections.targets.length >= 2; - } - return false; - }; - if (firstSelected) { - // Check if either selected point is from a Vehicle with max connections - if (checkVehicleConnections(firstSelected.pathUUID) || - checkVehicleConnections(pathUUID)) { - console.log("Vehicle already has maximum connections"); + // Check if trying to connect Vehicle to non-Conveyor + if ((firstPath?.type === 'Vehicle' && secondPath?.type !== 'Conveyor') || + (secondPath?.type === 'Vehicle' && firstPath?.type !== 'Conveyor')) { + console.log("Vehicle can only connect to Conveyors"); return; } - // Check if we're trying to add a second Conveyor connection to a Vehicle - if (firstPath?.type === 'Vehicle' && secondPath?.type === 'Conveyor') { - const hasConveyorConnection = firstPath.point.connections.targets.some(target => { - const targetPath = simulationPaths.find(p => p.modeluuid === target.pathUUID); - return targetPath?.type === 'Conveyor'; - }); - - if (hasConveyorConnection) { - console.log("Vehicle can only have one connection to a Conveyor"); - return; - } - } - - if (secondPath?.type === 'Vehicle' && firstPath?.type === 'Conveyor') { - const hasConveyorConnection = secondPath.point.connections.targets.some(target => { - const targetPath = simulationPaths.find(p => p.modeluuid === target.pathUUID); - return targetPath?.type === 'Conveyor'; - }); - - if (hasConveyorConnection) { - console.log("Vehicle can only have one connection to a Conveyor"); - return; - } - } - // Prevent same-path connections if (firstSelected.pathUUID === pathUUID) { console.log("Cannot connect spheres on the same path."); @@ -478,28 +438,19 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec return false; }); - // Check vehicle connection limits + // Check vehicle connection rules const isVehicleAtMaxConnections = pathData.type === 'Vehicle' && - pathData.point.connections.targets.length >= 2; - - const isVehicleConveyorConflict = - (firstPath?.type === 'Vehicle' && secondPath?.type === 'Conveyor' && - firstPath.point.connections.targets.some(t => { - const targetPath = simulationPaths.find(p => p.modeluuid === t.pathUUID); - return targetPath?.type === 'Conveyor'; - })) || - (secondPath?.type === 'Vehicle' && firstPath?.type === 'Conveyor' && - secondPath.point.connections.targets.some(t => { - const targetPath = simulationPaths.find(p => p.modeluuid === t.pathUUID); - return targetPath?.type === 'Conveyor'; - })); + pathData.point.connections.targets.length >= 1; + const isVehicleConnectingToNonConveyor = + (firstPath?.type === 'Vehicle' && secondPath?.type !== 'Conveyor') || + (secondPath?.type === 'Vehicle' && firstPath?.type !== 'Conveyor'); if ( !isDuplicateConnection && !isVehicleToVehicle && !isNonVehicleAlreadyConnected && !isVehicleAtMaxConnections && - !isVehicleConveyorConflict && + !isVehicleConnectingToNonConveyor && firstSelected.sphereUUID !== sphereUUID && firstSelected.pathUUID !== pathUUID && (firstSelected.isCorner || isConnectable) diff --git a/app/src/modules/simulation/path/pathCreation.tsx b/app/src/modules/simulation/path/pathCreation.tsx index 33ca2f8..202c7e7 100644 --- a/app/src/modules/simulation/path/pathCreation.tsx +++ b/app/src/modules/simulation/path/pathCreation.tsx @@ -1,8 +1,8 @@ import * as THREE from 'three'; import * as Types from '../../../types/world/worldTypes'; -import { useRef, useState, useEffect } from 'react'; +import { useRef, useState, useEffect, useMemo } from 'react'; import { Sphere, TransformControls } from '@react-three/drei'; -import { useIsConnecting, useRenderDistance, useSelectedActionSphere, useSelectedPath, useSimulationPaths } from '../../../store/store'; +import { useEditingPoint, useEyeDropMode, useIsConnecting, usePreviewPosition, useRenderDistance, useSelectedActionSphere, useSelectedPath, useSimulationPaths } from '../../../store/store'; import { useFrame, useThree } from '@react-three/fiber'; import { useSubModuleStore } from '../../../store/useModuleStore'; @@ -10,13 +10,18 @@ function PathCreation({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObject const { renderDistance } = useRenderDistance(); const { setSubModule } = useSubModuleStore(); const { setSelectedActionSphere, selectedActionSphere } = useSelectedActionSphere(); + const { eyeDropMode, setEyeDropMode } = useEyeDropMode(); + const { editingPoint, setEditingPoint } = useEditingPoint(); + const { previewPosition, setPreviewPosition } = usePreviewPosition(); + const { raycaster, camera, pointer, gl } = useThree(); + const plane = useMemo(() => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0), []); const { setSelectedPath } = useSelectedPath(); const { simulationPaths, setSimulationPaths } = useSimulationPaths(); const { isConnecting } = useIsConnecting(); - const { camera } = useThree(); - + const groupRefs = useRef<{ [key: string]: THREE.Group }>({}); const sphereRefs = useRef<{ [key: string]: THREE.Mesh }>({}); + const isMovingRef = useRef(false); const transformRef = useRef(null); const [transformMode, setTransformMode] = useState<'translate' | 'rotate' | null>(null); @@ -77,6 +82,83 @@ function PathCreation({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObject setSimulationPaths(updatedPaths); }; + useFrame(() => { + if (eyeDropMode) { + raycaster.setFromCamera(pointer, camera); + const intersectionPoint = new THREE.Vector3(); + const point = raycaster.ray.intersectPlane(plane, intersectionPoint); + + if (point) { + setPreviewPosition({ x: point.x, y: point.z }); + } + } else { + setPreviewPosition(null); + } + }); + + useEffect(() => { + if (!camera) return; + const canvasElement = gl.domElement; + canvasElement.tabIndex = 0; + + + const onPointerDown = () => { + isMovingRef.current = false; + }; + + const onPointerMove = () => { + isMovingRef.current = true; + }; + + const onPointerUp = (event: PointerEvent) => { + if (!isMovingRef.current && eyeDropMode && event.button === 0 && previewPosition) { + event.preventDefault(); + if (editingPoint) { + handlePointUpdate(editingPoint, previewPosition.x, previewPosition.y); + setEditingPoint(null); + setEyeDropMode(false); + } + } + }; + + if (eyeDropMode) { + canvasElement.addEventListener("pointerdown", onPointerDown); + canvasElement.addEventListener("pointermove", onPointerMove); + canvasElement.addEventListener("pointerup", onPointerUp); + } + + return () => { + canvasElement.removeEventListener("pointerdown", onPointerDown); + canvasElement.removeEventListener("pointermove", onPointerMove); + canvasElement.removeEventListener("pointerup", onPointerUp); + }; + }, [eyeDropMode, editingPoint, previewPosition]); + + const handlePointUpdate = (pointType: 'start' | 'end', x: number, z: number) => { + if (!selectedActionSphere?.point?.uuid) return; + + const updatedPaths = simulationPaths.map((path) => { + if (path.type === "Vehicle" && path.point.uuid === selectedActionSphere.point.uuid) { + return { + ...path, + point: { + ...path.point, + actions: { + ...path.point.actions, + [pointType]: { + ...path.point.actions[pointType], + x: x, + y: z + } + } + } + }; + } + return path; + }); + + setSimulationPaths(updatedPaths); + }; return ( @@ -92,7 +174,7 @@ function PathCreation({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObject position={path.assetPosition} rotation={path.assetRotation} onClick={(e) => { - if (isConnecting) return; + if (isConnecting || eyeDropMode) return; e.stopPropagation(); setSelectedPath({ path, group: groupRefs.current[path.modeluuid] }); setSelectedActionSphere(null); @@ -100,6 +182,7 @@ function PathCreation({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObject setSubModule('mechanics'); }} onPointerMissed={() => { + if (eyeDropMode) return; setSelectedPath(null); setSubModule('properties'); }} @@ -113,7 +196,7 @@ function PathCreation({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObject name='events-sphere' ref={el => (sphereRefs.current[point.uuid] = el!)} onClick={(e) => { - if (isConnecting) return; + if (isConnecting || eyeDropMode) return; e.stopPropagation(); setSelectedActionSphere({ path, @@ -124,6 +207,7 @@ function PathCreation({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObject }} userData={{ point, path }} onPointerMissed={() => { + if (eyeDropMode) return; setSubModule('properties'); setSelectedActionSphere(null); }} @@ -155,7 +239,7 @@ function PathCreation({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObject ref={el => (groupRefs.current[path.modeluuid] = el!)} position={path.assetPosition} onClick={(e) => { - if (isConnecting) return; + if (isConnecting || eyeDropMode) return; e.stopPropagation(); setSelectedPath({ path, group: groupRefs.current[path.modeluuid] }); setSelectedActionSphere(null); @@ -163,6 +247,7 @@ function PathCreation({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObject setSubModule('mechanics'); }} onPointerMissed={() => { + if (eyeDropMode) return; setSelectedPath(null); setSubModule('properties'); }} @@ -175,7 +260,7 @@ function PathCreation({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObject name='events-sphere' ref={el => (sphereRefs.current[path.point.uuid] = el!)} onClick={(e) => { - if (isConnecting) return; + if (isConnecting || eyeDropMode) return; e.stopPropagation(); setSelectedActionSphere({ path, @@ -186,6 +271,7 @@ function PathCreation({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObject }} userData={{ point: path.point, path }} onPointerMissed={() => { + if (eyeDropMode) return; setSubModule('properties'); setSelectedActionSphere(null); }} diff --git a/app/src/modules/simulation/simulation.tsx b/app/src/modules/simulation/simulation.tsx index 10934fb..8e7a214 100644 --- a/app/src/modules/simulation/simulation.tsx +++ b/app/src/modules/simulation/simulation.tsx @@ -1,6 +1,5 @@ -import { useState, useEffect, useRef } from 'react'; +import { useState, useEffect, useRef, useMemo } from 'react'; import { useSelectedActionSphere, useSelectedPath, useSimulationPaths } from '../../store/store'; -import { useThree } from '@react-three/fiber'; import * as THREE from 'three'; import Behaviour from './behaviour/behaviour'; import PathCreation from './path/pathCreation'; @@ -29,9 +28,10 @@ function Simulation() { // } // }, [selectedPath]); + return ( <> - + {activeModule === 'simulation' && ( <> diff --git a/app/src/services/simulation/getAssetEventType.ts b/app/src/services/simulation/getAssetEventType.ts new file mode 100644 index 0000000..e516feb --- /dev/null +++ b/app/src/services/simulation/getAssetEventType.ts @@ -0,0 +1,26 @@ +let url_Backend_dwinzo = `http://${process.env.REACT_APP_TEST}`; + +export const getAssetEventType = async (modelId: string, organization: string) => { + try { + const response = await fetch(`${url_Backend_dwinzo}/api/v2/pointData/${modelId}/${organization}`, { + method: "GET", + headers: { + "Content-Type": "application/json", + }, + }); + + if (!response.ok) { + throw new Error("Failed to fetch model event type"); + } + + const result = await response.json(); + console.log('result: ', result); + return result; + } catch (error) { + if (error instanceof Error) { + throw new Error(error.message); + } else { + throw new Error("An unknown error occurred"); + } + } +}; \ No newline at end of file diff --git a/app/src/store/store.ts b/app/src/store/store.ts index a64f417..9688448 100644 --- a/app/src/store/store.ts +++ b/app/src/store/store.ts @@ -348,10 +348,30 @@ export const useStartSimulation = create((set: any) => ({ startSimulation: false, setStartSimulation: (x: any) => set({ startSimulation: x }), })); + +export const useEyeDropMode = create((set: any) => ({ + eyeDropMode: false, + setEyeDropMode: (x: any) => set({ eyeDropMode: x }), +})); + +export const useEditingPoint = create((set: any) => ({ + editingPoint: false, + setEditingPoint: (x: any) => set({ editingPoint: x }), +})); + +export const usePreviewPosition = create<{ + previewPosition: { x: number; y: number } | null; + setPreviewPosition: (position: { x: number; y: number } | null) => void; +}>((set) => ({ + previewPosition: null, + setPreviewPosition: (position) => set({ previewPosition: position }), +})); + export const usezoneTarget = create((set: any) => ({ zoneTarget: [], setZoneTarget: (x: any) => set({ zoneTarget: x }), })); + export const usezonePosition = create((set: any) => ({ zonePosition: [], setZonePosition: (x: any) => set({ zonePosition: x }), @@ -371,6 +391,7 @@ export const useAsset3dWidget = create((set: any) => ({ widgetSelect: "", setWidgetSelect: (x: any) => set({ widgetSelect: x }), })); + export const useWidgetSubOption = create((set: any) => ({ widgetSubOption: "2D", setWidgetSubOption: (x: any) => set({ widgetSubOption: x }), diff --git a/app/src/types/world/worldTypes.d.ts b/app/src/types/world/worldTypes.d.ts index 31c032c..3e40f40 100644 --- a/app/src/types/world/worldTypes.d.ts +++ b/app/src/types/world/worldTypes.d.ts @@ -310,7 +310,7 @@ interface VehicleEventsSchema { point: { uuid: string; position: [number, number, number]; - actions: { uuid: string; name: string; type: string; start: string, hitCount: number, end: string, buffer: number }; + actions: { uuid: string; name: string; type: string; start: { x: number, y: number } | {}, hitCount: number, end: { x: number, y: number } | {}, buffer: number }; connections: { source: { pathUUID: string; pointUUID: string }; targets: { pathUUID: string; pointUUID: string }[] }; speed: number; }; From e54c9e6e0dd718af44ed983ec21d382a01d05b76 Mon Sep 17 00:00:00 2001 From: Jerald-Golden-B Date: Mon, 31 Mar 2025 14:52:06 +0530 Subject: [PATCH 03/11] Refactor Tools component for improved readability and structure --- app/src/components/ui/Tools.tsx | 88 +++++++++++++++------------------ 1 file changed, 40 insertions(+), 48 deletions(-) diff --git a/app/src/components/ui/Tools.tsx b/app/src/components/ui/Tools.tsx index 012142e..3333282 100644 --- a/app/src/components/ui/Tools.tsx +++ b/app/src/components/ui/Tools.tsx @@ -68,7 +68,7 @@ const Tools: React.FC = () => { : true ); }, []); - useEffect(() => {}, [activeModule]); + useEffect(() => { }, [activeModule]); useEffect(() => { setActiveTool(activeSubTool); setActiveSubTool(activeSubTool); @@ -210,9 +210,8 @@ const Tools: React.FC = () => {
{activeSubTool == "cursor" && (
{ setActiveTool("cursor"); }} @@ -222,9 +221,8 @@ const Tools: React.FC = () => { )} {activeSubTool == "free-hand" && (
{ setActiveTool("free-hand"); }} @@ -234,9 +232,8 @@ const Tools: React.FC = () => { )} {activeSubTool == "delete" && (
{ setActiveTool("delete"); }} @@ -308,9 +305,8 @@ const Tools: React.FC = () => {
{ setActiveTool("draw-wall"); }} @@ -319,9 +315,8 @@ const Tools: React.FC = () => {
{ setActiveTool("draw-zone"); }} @@ -330,9 +325,8 @@ const Tools: React.FC = () => {
{ setActiveTool("draw-aisle"); }} @@ -341,9 +335,8 @@ const Tools: React.FC = () => {
{ setActiveTool("draw-floor"); }} @@ -359,9 +352,8 @@ const Tools: React.FC = () => {
{ setActiveTool("measure"); }} @@ -377,9 +369,8 @@ const Tools: React.FC = () => {
{ setActiveTool("pen"); }} @@ -411,9 +402,8 @@ const Tools: React.FC = () => {
{ setActiveTool("comment"); }} @@ -422,9 +412,8 @@ const Tools: React.FC = () => {
{toggleThreeD && (
{ setIsPlaying(!isPlaying); }} @@ -433,20 +422,23 @@ const Tools: React.FC = () => {
)}
-
-
-
- 2d -
-
- 3d -
-
+ {activeModule === "builder" && ( + <> +
+
+
+ 2d +
+
+ 3d +
+
+ + )}
) : ( From ec87acb82461688ad2efa4a14443b57a1ec57bdd Mon Sep 17 00:00:00 2001 From: gabriel Date: Mon, 31 Mar 2025 15:02:35 +0530 Subject: [PATCH 04/11] added input for name --- app/.env | 2 + app/package-lock.json | 38 ++-- app/package.json | 1 + .../IotInputCards/LineGrapInput.tsx | 77 ++++--- .../sidebarRight/visualization/data/Data.tsx | 22 +- .../ui/componets/DraggableWidget.tsx | 9 +- .../realTimeVis/charts/BarGraphComponent.tsx | 19 +- .../charts/DoughnutGraphComponent.tsx | 202 +++++++++++++----- .../realTimeVis/charts/LineGraphComponent.tsx | 16 +- .../realTimeVis/charts/PieGraphComponent.tsx | 16 +- .../zoneData/addWidgets.ts | 2 +- .../zoneData/getSelect2dZoneData.ts | 2 +- .../zoneData/getZoneData.ts | 2 +- .../realTimeVisulization/zoneData/getZones.ts | 2 +- app/src/store/useChartStore.ts | 6 + 15 files changed, 281 insertions(+), 135 deletions(-) diff --git a/app/.env b/app/.env index 153338d..c93e3d1 100644 --- a/app/.env +++ b/app/.env @@ -7,6 +7,8 @@ REACT_APP_SERVER_SOCKET_API_BASE_URL=185.100.212.76:8000 # Base URL for the server REST API, used for HTTP requests to the backend server. REACT_APP_SERVER_REST_API_BASE_URL=185.100.212.76:5000 +REACT_APP_SERVER_REST_API_LOCAL_BASE_URL=192.168.0.102:5000 + # Base URL for the server marketplace API. REACT_APP_SERVER_MARKETPLACE_URL=185.100.212.76:50011 diff --git a/app/package-lock.json b/app/package-lock.json index d3aa21e..7d72590 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -26,6 +26,7 @@ "@types/react-dom": "^18.3.0", "@use-gesture/react": "^10.3.1", "chart.js": "^4.4.8", + "chartjs-plugin-annotation": "^3.1.0", "glob": "^11.0.0", "gsap": "^3.12.5", "leva": "^0.10.0", @@ -2018,7 +2019,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==", - "dev": true, + "devOptional": true, "dependencies": { "@jridgewell/trace-mapping": "0.3.9" }, @@ -2030,7 +2031,7 @@ "version": "0.3.9", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "dev": true, + "devOptional": true, "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" @@ -4263,25 +4264,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==", - "dev": true + "devOptional": 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==", - "dev": true + "devOptional": 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==", - "dev": true + "devOptional": 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==", - "dev": true + "devOptional": true }, "node_modules/@turf/along": { "version": "7.2.0", @@ -8498,6 +8499,15 @@ "pnpm": ">=8" } }, + "node_modules/chartjs-plugin-annotation": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/chartjs-plugin-annotation/-/chartjs-plugin-annotation-3.1.0.tgz", + "integrity": "sha512-EkAed6/ycXD/7n0ShrlT1T2Hm3acnbFhgkIEJLa0X+M6S16x0zwj1Fv4suv/2bwayCT3jGPdAtI9uLcAMToaQQ==", + "license": "MIT", + "peerDependencies": { + "chart.js": ">=4.0.0" + } + }, "node_modules/check-more-types": { "version": "2.24.0", "resolved": "https://registry.npmjs.org/check-more-types/-/check-more-types-2.24.0.tgz", @@ -9006,7 +9016,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==", - "dev": true + "devOptional": true }, "node_modules/cross-env": { "version": "7.0.3", @@ -9874,7 +9884,7 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true, + "devOptional": true, "engines": { "node": ">=0.3.1" } @@ -15224,7 +15234,7 @@ "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true + "devOptional": true }, "node_modules/makeerror": { "version": "1.0.12", @@ -20683,7 +20693,7 @@ "version": "10.9.2", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", - "dev": true, + "devOptional": true, "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -20726,7 +20736,7 @@ "version": "8.3.4", "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", - "dev": true, + "devOptional": true, "dependencies": { "acorn": "^8.11.0" }, @@ -20738,7 +20748,7 @@ "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true + "devOptional": true }, "node_modules/tsconfig-paths": { "version": "3.15.0", @@ -21225,7 +21235,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==", - "dev": true + "devOptional": true }, "node_modules/v8-to-istanbul": { "version": "8.1.1", @@ -22284,7 +22294,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true, + "devOptional": true, "engines": { "node": ">=6" } diff --git a/app/package.json b/app/package.json index 541d3a1..66e3b39 100644 --- a/app/package.json +++ b/app/package.json @@ -21,6 +21,7 @@ "@types/react-dom": "^18.3.0", "@use-gesture/react": "^10.3.1", "chart.js": "^4.4.8", + "chartjs-plugin-annotation": "^3.1.0", "glob": "^11.0.0", "gsap": "^3.12.5", "leva": "^0.10.0", diff --git a/app/src/components/layout/sidebarRight/visualization/IotInputCards/LineGrapInput.tsx b/app/src/components/layout/sidebarRight/visualization/IotInputCards/LineGrapInput.tsx index baf1fcd..f36207e 100644 --- a/app/src/components/layout/sidebarRight/visualization/IotInputCards/LineGrapInput.tsx +++ b/app/src/components/layout/sidebarRight/visualization/IotInputCards/LineGrapInput.tsx @@ -58,11 +58,11 @@ // name: string; // fields: string; // } - + // interface InputData { // [key: string]: Measurement; // } - + // const extractMeasurements = (input: InputData): Measurement[] => { // return Object.values(input); // }; @@ -71,7 +71,7 @@ // const measurementsData = extractMeasurements(selections); // setMeasurements(measurementsData); // }, [selections]); - + // return ( // <> @@ -125,20 +125,22 @@ import useChartStore from "../../../../../store/useChartStore"; import { useSelectedZoneStore } from "../../../../../store/useZoneStore"; import { useWidgetStore } from "../../../../../store/useWidgetStore"; import axios from "axios"; +import RenameInput from "../../../../ui/inputs/RenameInput"; type Props = {}; const LineGrapInput = (props: Props) => { - const { setMeasurements, updateDuration } = useChartStore(); + const [widgetName, setWidgetName] = useState('Widget'); + const { setMeasurements, updateDuration, updateName } = useChartStore(); const [duration, setDuration] = useState('1h') const [dropDowndata, setDropDownData] = useState({}); - const [selections, setSelections] = useState>({}); + const [selections, setSelections] = useState>({}); const { selectedZone } = useSelectedZoneStore(); const { selectedChartId } = useWidgetStore(); const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL; const email = localStorage.getItem("email") || ""; const organization = email?.split("@")[1]?.split(".")[0] - + useEffect(() => { const fetchZoneData = async () => { try { @@ -157,13 +159,14 @@ const LineGrapInput = (props: Props) => { }, []); useEffect(() => { - const fetchSavedInputes = async() => { + const fetchSavedInputes = async () => { if (selectedChartId.id !== "") { try { - const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/WidgetData/${selectedChartId.id}/${organization}`); + const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_LOCAL_BASE_URL}/api/v2/WidgetData/${selectedChartId.id}/${organization}`); if (response.status === 200) { setSelections(response.data.Data.measurements) setDuration(response.data.Data.duration) + setWidgetName(response.data.widgetName) } else { console.log("Unexpected response:", response); } @@ -181,17 +184,19 @@ const LineGrapInput = (props: Props) => { useEffect(() => { setMeasurements(selections); updateDuration(duration); - }, [selections, duration]); + updateName(widgetName); + }, [selections, duration, widgetName]); - const sendInputes = async(inputMeasurement: any, inputDuration: any) => { + const sendInputes = async (inputMeasurement: any, inputDuration: any, inputName: any) => { try { - const response = await axios.post(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/widget/save`,{ + const response = await axios.post(`http://${process.env.REACT_APP_SERVER_REST_API_LOCAL_BASE_URL}/api/v2/widget/save`, { organization: organization, zoneId: selectedZone.zoneId, - widget:{ + widget: { id: selectedChartId.id, panel: selectedChartId.panel, + widgetName: inputName, Data: { measurements: inputMeasurement, duration: inputDuration @@ -210,35 +215,47 @@ const LineGrapInput = (props: Props) => { } } - const handleSelect = async(inputKey: string, selectedData: { name: string; fields: string } | null) => { - - // async() => { - const newSelections = { ...selections }; - if (selectedData === null) { - delete newSelections[inputKey]; - } else { - newSelections[inputKey] = selectedData; - } - // setMeasurements(newSelections); // Update Zustand store - console.log(newSelections); - if ( await sendInputes(newSelections, duration)) { - setSelections(newSelections); - } - // sendInputes(newSelections, duration); // Send data to server - // return newSelections; + const handleSelect = async (inputKey: string, selectedData: { name: string; fields: string } | null) => { + + // async() => { + const newSelections = { ...selections }; + if (selectedData === null) { + delete newSelections[inputKey]; + } else { + newSelections[inputKey] = selectedData; + } + // setMeasurements(newSelections); // Update Zustand store + console.log(newSelections); + if (await sendInputes(newSelections, duration, widgetName)) { + setSelections(newSelections); + } + // sendInputes(newSelections, duration); // Send data to server + // return newSelections; // }; }; - const handleSelectDuration = async(option: string) => { - if ( await sendInputes(selections, option)) { + const handleSelectDuration = async (option: string) => { + if (await sendInputes(selections, option, widgetName)) { setDuration(option); } // setDuration(option); }; + const handleNameChange = async (name:any) => { + console.log('name change requested',name); + + if (await sendInputes(selections, duration, name)) { + setWidgetName(name); + } + } + return ( <>
+
+
Widget name
+ +
{[...Array(6)].map((_, index) => { const inputKey = `input${index + 1}`; return ( diff --git a/app/src/components/layout/sidebarRight/visualization/data/Data.tsx b/app/src/components/layout/sidebarRight/visualization/data/Data.tsx index 71847c7..1100954 100644 --- a/app/src/components/layout/sidebarRight/visualization/data/Data.tsx +++ b/app/src/components/layout/sidebarRight/visualization/data/Data.tsx @@ -3,6 +3,7 @@ import { useWidgetStore } from "../../../../../store/useWidgetStore"; import { AddIcon, RemoveIcon } from "../../../../icons/ExportCommonIcons"; import MultiLevelDropDown from "../../../../ui/inputs/MultiLevelDropDown"; import LineGrapInput from "../IotInputCards/LineGrapInput"; +import RenameInput from "../../../../ui/inputs/RenameInput"; // Define the data structure for demonstration purposes const DATA_STRUCTURE = { @@ -107,27 +108,32 @@ const Data = () => { [selectedChartId.id]: currentChartData.map((group) => group.id === groupId ? { - ...group, - children: group.children.filter( - (child) => child.id !== childId - ), - } + ...group, + children: group.children.filter( + (child) => child.id !== childId + ), + } : group ), }; }); }; + console.log("selectedChartId", selectedChartId); return (
- {selectedChartId?.title && ( + {/* {selectedChartId?.title && (
{selectedChartId?.title}
- )} + )} */} + + + {/* */} + {/* Render groups dynamically */} { chartDataGroups[selectedChartId?.id] && } - + {/* Info Box */}
i diff --git a/app/src/components/ui/componets/DraggableWidget.tsx b/app/src/components/ui/componets/DraggableWidget.tsx index 9c17c03..f79c55c 100644 --- a/app/src/components/ui/componets/DraggableWidget.tsx +++ b/app/src/components/ui/componets/DraggableWidget.tsx @@ -287,18 +287,11 @@ export const DraggableWidget = ({ )} {widget.type === "doughnut" && ( )} {widget.type === "polarArea" && ( diff --git a/app/src/components/ui/realTimeVis/charts/BarGraphComponent.tsx b/app/src/components/ui/realTimeVis/charts/BarGraphComponent.tsx index 26f7a4b..8a18d0f 100644 --- a/app/src/components/ui/realTimeVis/charts/BarGraphComponent.tsx +++ b/app/src/components/ui/realTimeVis/charts/BarGraphComponent.tsx @@ -211,9 +211,10 @@ const BarGraphComponent = ({ fontWeight = "Regular", }: ChartComponentProps) => { const { themeColor } = useThemeStore(); - const { measurements: chartMeasurements, duration: chartDuration } = useChartStore(); + const { measurements: chartMeasurements, duration: chartDuration, name: widgetName } = useChartStore(); const [measurements, setmeasurements] = useState({}); const [duration, setDuration] = useState("1h") + const [name, setName] = useState("Widget") const [chartData, setChartData] = useState<{ labels: string[]; datasets: any[] }>({ labels: [], datasets: [], @@ -236,6 +237,10 @@ const BarGraphComponent = ({ ], }; + useEffect(() => { + console.log("titleeeeeeeeeeeeeeeeeee",title); + },[]) + // Memoize Theme Colors const buttonActionColor = useMemo(() => themeColor[0] || "#5c87df", [themeColor]); const buttonAbortColor = useMemo(() => themeColor[1] || "#ffffff", [themeColor]); @@ -270,7 +275,7 @@ const BarGraphComponent = ({ plugins: { title: { display: true, - text: title, + text: name, font: chartFontStyle, }, legend: { @@ -285,7 +290,7 @@ const BarGraphComponent = ({ }, }, }), - [title, chartFontStyle] + [title, chartFontStyle, name] ); // useEffect(() => {console.log(measurements); @@ -304,15 +309,12 @@ const BarGraphComponent = ({ const startStream = () => { - console.log("inputtttttttttt",inputData); socket.emit("lineInput", inputData); }; socket.on("connect", startStream); socket.on("lineOutput", (response) => { - console.log("responce dataaaaaaaaa",response.data); - const responseData = response.data; // Extract timestamps and values @@ -343,10 +345,11 @@ const BarGraphComponent = ({ if (id !== "") { try { - const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/WidgetData/${id}/${organization}`); + const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_LOCAL_BASE_URL}/api/v2/WidgetData/${id}/${organization}`); if (response.status === 200) { setmeasurements(response.data.Data.measurements) setDuration(response.data.Data.duration) + setName(response.data.widgetName) } else { console.log("Unexpected response:", response); } @@ -365,7 +368,7 @@ const BarGraphComponent = ({ fetchSavedInputes(); } } - ,[chartMeasurements, chartDuration]) + ,[chartMeasurements, chartDuration, widgetName]) return 0 ? chartData : defaultData} options={options} />; }; diff --git a/app/src/components/ui/realTimeVis/charts/DoughnutGraphComponent.tsx b/app/src/components/ui/realTimeVis/charts/DoughnutGraphComponent.tsx index c1d5ac2..8a6adf8 100644 --- a/app/src/components/ui/realTimeVis/charts/DoughnutGraphComponent.tsx +++ b/app/src/components/ui/realTimeVis/charts/DoughnutGraphComponent.tsx @@ -1,22 +1,64 @@ -import { useMemo } from "react"; -import { Doughnut, Line } from "react-chartjs-2"; +import React, { useEffect, useMemo, useState } from "react"; +import { Doughnut } from "react-chartjs-2"; +import io from "socket.io-client"; +import { useThemeStore } from "../../../../store/useThemeStore"; +import useChartStore from "../../../../store/useChartStore"; +import { useWidgetStore } from "../../../../store/useWidgetStore"; +import axios from "axios"; interface ChartComponentProps { + id: string; type: any; title: string; fontFamily?: string; fontSize?: string; fontWeight?: "Light" | "Regular" | "Bold"; - data: any; } const DoughnutGraphComponent = ({ + id, + type, title, fontFamily, fontSize, fontWeight = "Regular", }: ChartComponentProps) => { - // Memoize Font Weight Mapping + const { themeColor } = useThemeStore(); + const { measurements: chartMeasurements, duration: chartDuration, name: widgetName } = useChartStore(); + const [measurements, setmeasurements] = useState({}); + const [duration, setDuration] = useState("1h") + const [name, setName] = useState("Widget") + const [chartData, setChartData] = useState<{ labels: string[]; datasets: any[] }>({ + labels: [], + datasets: [], + }); + const { selectedChartId } = useWidgetStore(); + + const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL; + const email = localStorage.getItem("email") || ""; + const organization = email?.split("@")[1]?.split(".")[0] + const defaultData = { + labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"], + datasets: [ + { + label: "Dataset", + data: [12, 19, 3, 5, 2, 3], + backgroundColor: ["#6f42c1"], + borderColor: "#b392f0", + borderWidth: 1, + }, + ], + }; + + useEffect(() => { + console.log("titleeeeeeeeeeeeeeeeeee",title); + },[]) + + // Memoize Theme Colors + const buttonActionColor = useMemo(() => themeColor[0] || "#5c87df", [themeColor]); + const buttonAbortColor = useMemo(() => themeColor[1] || "#ffffff", [themeColor]); + + // Memoize Font Styling const chartFontWeightMap = useMemo( () => ({ Light: "lighter" as const, @@ -26,19 +68,9 @@ const DoughnutGraphComponent = ({ [] ); - // Parse and Memoize Font Size - const fontSizeValue = useMemo( - () => (fontSize ? parseInt(fontSize) : 12), - [fontSize] - ); + const fontSizeValue = useMemo(() => (fontSize ? parseInt(fontSize) : 12), [fontSize]); + const fontWeightValue = useMemo(() => chartFontWeightMap[fontWeight], [fontWeight, chartFontWeightMap]); - // Determine and Memoize Font Weight - const fontWeightValue = useMemo( - () => chartFontWeightMap[fontWeight], - [fontWeight, chartFontWeightMap] - ); - - // Memoize Chart Font Style const chartFontStyle = useMemo( () => ({ family: fontFamily || "Arial", @@ -48,46 +80,110 @@ const DoughnutGraphComponent = ({ [fontFamily, fontSizeValue, fontWeightValue] ); - const options = useMemo( - () => ({ - responsive: true, - maintainAspectRatio: false, - plugins: { - title: { - display: true, - text: title, - font: chartFontStyle, - }, - legend: { - display: false, - }, - }, - scales: { - x: { - ticks: { - display: false, // This hides the x-axis labels + // Memoize Chart Options + const options = useMemo( + () => ({ + responsive: true, + maintainAspectRatio: false, + plugins: { + title: { + display: true, + text: name, + font: chartFontStyle, + }, + legend: { + display: false, }, }, - }, - }), - [title, chartFontStyle] - ); + scales: { + // x: { + // ticks: { + // display: true, // This hides the x-axis labels + // }, + // }, + }, + }), + [title, chartFontStyle, name] + ); - const chartData = { - labels: ["January", "February", "March", "April", "May", "June", "July"], - datasets: [ - { - label: "My First Dataset", - data: [65, 59, 80, 81, 56, 55, 40], - backgroundColor: "#6f42c1", // Updated to #6f42c1 (Purple) - borderColor: "#ffffff", // Keeping border color white - borderWidth: 2, - fill: false, - }, - ], - }; + // useEffect(() => {console.log(measurements); + // },[measurements]) - return ; + useEffect(() => { + if (!iotApiUrl || !measurements || Object.keys(measurements).length === 0) return; + + const socket = io(`http://${iotApiUrl}`); + + const inputData = { + measurements, + duration, + interval: 1000, + }; + + + const startStream = () => { + socket.emit("lineInput", inputData); + }; + + socket.on("connect", startStream); + + socket.on("lineOutput", (response) => { + const responseData = response.data; + + // Extract timestamps and values + const labels = responseData.time; + const datasets = Object.keys(measurements).map((key) => { + const measurement = measurements[key]; + const datasetKey = `${measurement.name}.${measurement.fields}`; + return { + label: datasetKey, + data: responseData[datasetKey]?.values ?? [], + backgroundColor: "#6f42c1", + borderColor: "#b392f0", + borderWidth: 1, + }; + }); + + setChartData({ labels, datasets }); + }); + + return () => { + socket.off("lineOutput"); + socket.emit("stop_stream"); // Stop streaming when component unmounts + socket.disconnect(); + }; + }, [measurements, duration, iotApiUrl]); + + const fetchSavedInputes = async() => { + + if (id !== "") { + try { + const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_LOCAL_BASE_URL}/api/v2/WidgetData/${id}/${organization}`); + if (response.status === 200) { + setmeasurements(response.data.Data.measurements) + setDuration(response.data.Data.duration) + setName(response.data.widgetName) + } else { + console.log("Unexpected response:", response); + } + } catch (error) { + console.error("There was an error!", error); + } + } + } + + useEffect(() => { + fetchSavedInputes(); + }, []); + + useEffect(() => { + if (selectedChartId?.id === id) { + fetchSavedInputes(); + } + } + ,[chartMeasurements, chartDuration, widgetName]) + + return 0 ? chartData : defaultData} options={options} />; }; -export default DoughnutGraphComponent; +export default DoughnutGraphComponent; \ No newline at end of file diff --git a/app/src/components/ui/realTimeVis/charts/LineGraphComponent.tsx b/app/src/components/ui/realTimeVis/charts/LineGraphComponent.tsx index 34420fa..0425408 100644 --- a/app/src/components/ui/realTimeVis/charts/LineGraphComponent.tsx +++ b/app/src/components/ui/realTimeVis/charts/LineGraphComponent.tsx @@ -24,9 +24,10 @@ const LineGraphComponent = ({ fontWeight = "Regular", }: ChartComponentProps) => { const { themeColor } = useThemeStore(); - const { measurements: chartMeasurements, duration: chartDuration } = useChartStore(); + const { measurements: chartMeasurements, duration: chartDuration, name: widgetName } = useChartStore(); const [measurements, setmeasurements] = useState({}); const [duration, setDuration] = useState("1h") + const [name, setName] = useState("Widget") const [chartData, setChartData] = useState<{ labels: string[]; datasets: any[] }>({ labels: [], datasets: [], @@ -49,6 +50,10 @@ const LineGraphComponent = ({ ], }; + useEffect(() => { + console.log("titleeeeeeeeeeeeeeeeeee",title); + },[]) + // Memoize Theme Colors const buttonActionColor = useMemo(() => themeColor[0] || "#5c87df", [themeColor]); const buttonAbortColor = useMemo(() => themeColor[1] || "#ffffff", [themeColor]); @@ -83,7 +88,7 @@ const LineGraphComponent = ({ plugins: { title: { display: true, - text: title, + text: name, font: chartFontStyle, }, legend: { @@ -98,7 +103,7 @@ const LineGraphComponent = ({ }, }, }), - [title, chartFontStyle] + [title, chartFontStyle, name] ); // useEffect(() => {console.log(measurements); @@ -153,10 +158,11 @@ const LineGraphComponent = ({ if (id !== "") { try { - const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/WidgetData/${id}/${organization}`); + const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_LOCAL_BASE_URL}/api/v2/WidgetData/${id}/${organization}`); if (response.status === 200) { setmeasurements(response.data.Data.measurements) setDuration(response.data.Data.duration) + setName(response.data.widgetName) } else { console.log("Unexpected response:", response); } @@ -175,7 +181,7 @@ const LineGraphComponent = ({ fetchSavedInputes(); } } - ,[chartMeasurements, chartDuration]) + ,[chartMeasurements, chartDuration, widgetName]) return 0 ? chartData : defaultData} options={options} />; }; diff --git a/app/src/components/ui/realTimeVis/charts/PieGraphComponent.tsx b/app/src/components/ui/realTimeVis/charts/PieGraphComponent.tsx index 46516fa..8f32791 100644 --- a/app/src/components/ui/realTimeVis/charts/PieGraphComponent.tsx +++ b/app/src/components/ui/realTimeVis/charts/PieGraphComponent.tsx @@ -210,9 +210,10 @@ const PieChartComponent = ({ fontWeight = "Regular", }: ChartComponentProps) => { const { themeColor } = useThemeStore(); - const { measurements: chartMeasurements, duration: chartDuration } = useChartStore(); + const { measurements: chartMeasurements, duration: chartDuration, name: widgetName } = useChartStore(); const [measurements, setmeasurements] = useState({}); const [duration, setDuration] = useState("1h") + const [name, setName] = useState("Widget") const [chartData, setChartData] = useState<{ labels: string[]; datasets: any[] }>({ labels: [], datasets: [], @@ -235,6 +236,10 @@ const PieChartComponent = ({ ], }; + useEffect(() => { + console.log("titleeeeeeeeeeeeeeeeeee",title); + },[]) + // Memoize Theme Colors const buttonActionColor = useMemo(() => themeColor[0] || "#5c87df", [themeColor]); const buttonAbortColor = useMemo(() => themeColor[1] || "#ffffff", [themeColor]); @@ -269,7 +274,7 @@ const PieChartComponent = ({ plugins: { title: { display: true, - text: title, + text: name, font: chartFontStyle, }, legend: { @@ -284,7 +289,7 @@ const PieChartComponent = ({ // }, }, }), - [title, chartFontStyle] + [title, chartFontStyle, name] ); // useEffect(() => {console.log(measurements); @@ -339,10 +344,11 @@ const PieChartComponent = ({ if (id !== "") { try { - const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/WidgetData/${id}/${organization}`); + const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_LOCAL_BASE_URL}/api/v2/WidgetData/${id}/${organization}`); if (response.status === 200) { setmeasurements(response.data.Data.measurements) setDuration(response.data.Data.duration) + setName(response.data.widgetName) } else { console.log("Unexpected response:", response); } @@ -361,7 +367,7 @@ const PieChartComponent = ({ fetchSavedInputes(); } } - ,[chartMeasurements, chartDuration]) + ,[chartMeasurements, chartDuration, widgetName]) return 0 ? chartData : defaultData} options={options} />; }; diff --git a/app/src/services/realTimeVisulization/zoneData/addWidgets.ts b/app/src/services/realTimeVisulization/zoneData/addWidgets.ts index 683943e..64be803 100644 --- a/app/src/services/realTimeVisulization/zoneData/addWidgets.ts +++ b/app/src/services/realTimeVisulization/zoneData/addWidgets.ts @@ -1,4 +1,4 @@ -let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`; +let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_LOCAL_BASE_URL}`; export const addingWidgets = async ( zoneId: string, diff --git a/app/src/services/realTimeVisulization/zoneData/getSelect2dZoneData.ts b/app/src/services/realTimeVisulization/zoneData/getSelect2dZoneData.ts index 71d9c2f..f509a5d 100644 --- a/app/src/services/realTimeVisulization/zoneData/getSelect2dZoneData.ts +++ b/app/src/services/realTimeVisulization/zoneData/getSelect2dZoneData.ts @@ -1,4 +1,4 @@ -let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`; +let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_LOCAL_BASE_URL}`; console.log("url_Backend_dwinzo: ", url_Backend_dwinzo); export const getSelect2dZoneData = async ( diff --git a/app/src/services/realTimeVisulization/zoneData/getZoneData.ts b/app/src/services/realTimeVisulization/zoneData/getZoneData.ts index d2df867..5d84167 100644 --- a/app/src/services/realTimeVisulization/zoneData/getZoneData.ts +++ b/app/src/services/realTimeVisulization/zoneData/getZoneData.ts @@ -1,4 +1,4 @@ -let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`; +let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_LOCAL_BASE_URL}`; export const getZone2dData = async (organization?: string) => { try { diff --git a/app/src/services/realTimeVisulization/zoneData/getZones.ts b/app/src/services/realTimeVisulization/zoneData/getZones.ts index 39031d8..32f850a 100644 --- a/app/src/services/realTimeVisulization/zoneData/getZones.ts +++ b/app/src/services/realTimeVisulization/zoneData/getZones.ts @@ -1,4 +1,4 @@ -let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`; +let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_LOCAL_BASE_URL}`; export const getZoneData = async (zoneId: string, organization: string) => { try { diff --git a/app/src/store/useChartStore.ts b/app/src/store/useChartStore.ts index 4d61952..079b9ff 100644 --- a/app/src/store/useChartStore.ts +++ b/app/src/store/useChartStore.ts @@ -9,20 +9,26 @@ interface MeasurementStore { measurements: Record; // Change array to Record interval: number; duration: string; + name: string; setMeasurements: (newMeasurements: Record) => void; updateDuration: (newDuration: string) => void; + updateName: (newName: string) => void; } const useChartStore = create((set) => ({ measurements: {}, // Initialize as an empty object interval: 1000, duration: "1h", + name:'', setMeasurements: (newMeasurements) => set(() => ({ measurements: newMeasurements })), updateDuration: (newDuration) => set(() => ({ duration: newDuration })), + + updateName: (newName) => + set(() => ({ duration: newName })), })); export default useChartStore; From b125989ae7384800bf9ed1fcaa563a8ca067d8bb Mon Sep 17 00:00:00 2001 From: Vishnu Date: Mon, 31 Mar 2025 18:06:44 +0530 Subject: [PATCH 05/11] Refactor styles for improved theme management, enhance input components, and add scene styles --- app/public/index.html | 9 +- .../components/layout/sidebarRight/Header.tsx | 13 +- .../mechanics/ConveyorMechanics.tsx | 485 ++++++++++-------- app/src/components/ui/menu/menu.tsx | 24 + .../geomentries/lines/distanceText.tsx | 203 +++++--- app/src/modules/builder/groups/zoneGroup.tsx | 4 +- app/src/modules/scene/environment/ground.tsx | 6 +- app/src/modules/scene/scene.tsx | 28 +- app/src/styles/abstracts/variables.scss | 3 +- app/src/styles/base/base.scss | 2 +- app/src/styles/components/input.scss | 35 +- app/src/styles/components/menu/menu.scss | 13 +- app/src/styles/layout/sidebar.scss | 8 + app/src/styles/main.scss | 7 +- app/src/styles/pages/realTimeViz.scss | 3 +- app/src/styles/scene/scene.scss | 23 + app/src/types/world/worldConstants.ts | 26 +- app/src/utils/theme.ts | 35 +- 18 files changed, 556 insertions(+), 371 deletions(-) create mode 100644 app/src/styles/scene/scene.scss diff --git a/app/public/index.html b/app/public/index.html index 785ee0f..d05ca9d 100644 --- a/app/public/index.html +++ b/app/public/index.html @@ -1,9 +1,12 @@ - + - + Dwinzo (beta) - +
diff --git a/app/src/components/layout/sidebarRight/Header.tsx b/app/src/components/layout/sidebarRight/Header.tsx index 1da29a2..e51bf9d 100644 --- a/app/src/components/layout/sidebarRight/Header.tsx +++ b/app/src/components/layout/sidebarRight/Header.tsx @@ -13,7 +13,7 @@ const Header: React.FC = () => { const guestUsers: ActiveUser[] = activeUsers.filter( (user: ActiveUser) => user.userName !== userName ); - + const [userManagement, setUserManagement] = useState(false); return ( @@ -31,9 +31,9 @@ const Header: React.FC = () => { > Share
-
+ {/*
-
+
*/}
@@ -52,12 +52,7 @@ const Header: React.FC = () => { ))}
-
- {userName[0]} -
+
{userName[0]}
diff --git a/app/src/components/layout/sidebarRight/mechanics/ConveyorMechanics.tsx b/app/src/components/layout/sidebarRight/mechanics/ConveyorMechanics.tsx index f80fca0..bd10613 100644 --- a/app/src/components/layout/sidebarRight/mechanics/ConveyorMechanics.tsx +++ b/app/src/components/layout/sidebarRight/mechanics/ConveyorMechanics.tsx @@ -9,9 +9,13 @@ import RenameInput from "../../../ui/inputs/RenameInput"; import InputWithDropDown from "../../../ui/inputs/InputWithDropDown"; import LabledDropdown from "../../../ui/inputs/LabledDropdown"; import { handleResize } from "../../../../functions/handleResizePannel"; -import { useSelectedActionSphere, useSelectedPath, useSimulationPaths } from "../../../../store/store"; -import * as THREE from 'three'; -import * as Types from '../../../../types/world/worldTypes'; +import { + useSelectedActionSphere, + useSelectedPath, + useSimulationPaths, +} from "../../../../store/store"; +import * as THREE from "three"; +import * as Types from "../../../../types/world/worldTypes"; import InputToggle from "../../../ui/inputs/InputToggle"; const ConveyorMechanics: React.FC = () => { @@ -25,7 +29,9 @@ const ConveyorMechanics: React.FC = () => { const selectedPoint = useMemo(() => { if (!selectedActionSphere) return null; return simulationPaths - .filter((path): path is Types.ConveyorEventsSchema => path.type === "Conveyor") + .filter( + (path): path is Types.ConveyorEventsSchema => path.type === "Conveyor" + ) .flatMap((path) => path.points) .find((point) => point.uuid === selectedActionSphere.point.uuid); }, [selectedActionSphere, simulationPaths]); @@ -43,11 +49,11 @@ const ConveyorMechanics: React.FC = () => { const newAction = { uuid: THREE.MathUtils.generateUUID(), name: `Action ${actionIndex + 1}`, - type: 'Inherit', - material: 'Inherit', - delay: 'Inherit', - spawnInterval: 'Inherit', - isUsed: false + type: "Inherit", + material: "Inherit", + delay: "Inherit", + spawnInterval: "Inherit", + isUsed: false, }; return { ...point, actions: [...point.actions, newAction] }; @@ -68,13 +74,18 @@ const ConveyorMechanics: React.FC = () => { const updatedPaths = simulationPaths.map((path) => path.type === "Conveyor" ? { - ...path, - points: path.points.map((point) => - point.uuid === selectedActionSphere.point.uuid - ? { ...point, actions: point.actions.filter(action => action.uuid !== uuid) } - : point - ), - } + ...path, + points: path.points.map((point) => + point.uuid === selectedActionSphere.point.uuid + ? { + ...point, + actions: point.actions.filter( + (action) => action.uuid !== uuid + ), + } + : point + ), + } : path ); @@ -87,26 +98,33 @@ const ConveyorMechanics: React.FC = () => { const updatedPaths = simulationPaths.map((path) => path.type === "Conveyor" ? { - ...path, - points: path.points.map((point) => - point.uuid === selectedActionSphere.point.uuid - ? { - ...point, - actions: point.actions.map((action) => - action.uuid === uuid - ? { - ...action, - type: actionType, - material: actionType === 'Spawn' || actionType === 'Swap' ? 'Inherit' : action.material, - delay: actionType === 'Delay' ? 'Inherit' : action.delay, - spawnInterval: actionType === 'Spawn' ? 'Inherit' : action.spawnInterval - } - : action - ), - } - : point - ), - } + ...path, + points: path.points.map((point) => + point.uuid === selectedActionSphere.point.uuid + ? { + ...point, + actions: point.actions.map((action) => + action.uuid === uuid + ? { + ...action, + type: actionType, + material: + actionType === "Spawn" || actionType === "Swap" + ? "Inherit" + : action.material, + delay: + actionType === "Delay" ? "Inherit" : action.delay, + spawnInterval: + actionType === "Spawn" + ? "Inherit" + : action.spawnInterval, + } + : action + ), + } + : point + ), + } : path ); @@ -115,15 +133,17 @@ const ConveyorMechanics: React.FC = () => { // Update the selected item to reflect changes if (selectedItem?.type === "action" && selectedItem.item.uuid === uuid) { const updatedAction = updatedPaths - .filter((path): path is Types.ConveyorEventsSchema => path.type === "Conveyor") - .flatMap(path => path.points) - .find(p => p.uuid === selectedActionSphere.point.uuid) - ?.actions.find(a => a.uuid === uuid); + .filter( + (path): path is Types.ConveyorEventsSchema => path.type === "Conveyor" + ) + .flatMap((path) => path.points) + .find((p) => p.uuid === selectedActionSphere.point.uuid) + ?.actions.find((a) => a.uuid === uuid); if (updatedAction) { setSelectedItem({ type: "action", - item: updatedAction + item: updatedAction, }); } } @@ -136,21 +156,21 @@ const ConveyorMechanics: React.FC = () => { const updatedPaths = simulationPaths.map((path) => path.type === "Conveyor" ? { - ...path, - points: path.points.map((point) => - point.uuid === selectedActionSphere.point.uuid - ? { - ...point, - actions: point.actions.map((action) => - action.uuid === uuid && - (action.type === 'Spawn' || action.type === 'Swap') - ? { ...action, material } - : action - ), - } - : point - ), - } + ...path, + points: path.points.map((point) => + point.uuid === selectedActionSphere.point.uuid + ? { + ...point, + actions: point.actions.map((action) => + action.uuid === uuid && + (action.type === "Spawn" || action.type === "Swap") + ? { ...action, material } + : action + ), + } + : point + ), + } : path ); @@ -162,8 +182,8 @@ const ConveyorMechanics: React.FC = () => { ...selectedItem, item: { ...selectedItem.item, - material - } + material, + }, }); } }; @@ -174,42 +194,47 @@ const ConveyorMechanics: React.FC = () => { const updatedPaths = simulationPaths.map((path) => path.type === "Conveyor" ? { - ...path, - points: path.points.map((point) => - point.uuid === selectedActionSphere.point.uuid - ? { - ...point, - actions: point.actions.map((action) => - action.uuid === uuid ? { ...action, delay } : action - ), - } - : point - ), - } + ...path, + points: path.points.map((point) => + point.uuid === selectedActionSphere.point.uuid + ? { + ...point, + actions: point.actions.map((action) => + action.uuid === uuid ? { ...action, delay } : action + ), + } + : point + ), + } : path ); setSimulationPaths(updatedPaths); }; - const handleSpawnIntervalChange = (uuid: string, spawnInterval: number | string) => { + const handleSpawnIntervalChange = ( + uuid: string, + spawnInterval: number | string + ) => { if (!selectedActionSphere) return; const updatedPaths = simulationPaths.map((path) => path.type === "Conveyor" ? { - ...path, - points: path.points.map((point) => - point.uuid === selectedActionSphere.point.uuid - ? { - ...point, - actions: point.actions.map((action) => - action.uuid === uuid ? { ...action, spawnInterval } : action - ), - } - : point - ), - } + ...path, + points: path.points.map((point) => + point.uuid === selectedActionSphere.point.uuid + ? { + ...point, + actions: point.actions.map((action) => + action.uuid === uuid + ? { ...action, spawnInterval } + : action + ), + } + : point + ), + } : path ); @@ -233,23 +258,23 @@ const ConveyorMechanics: React.FC = () => { const updatedPaths = simulationPaths.map((path) => path.type === "Conveyor" ? { - ...path, - points: path.points.map((point) => { - if (point.uuid === selectedActionSphere.point.uuid) { - const triggerIndex = point.triggers.length; - const newTrigger = { - uuid: THREE.MathUtils.generateUUID(), - name: `Trigger ${triggerIndex + 1}`, - type: '', - bufferTime: 0, - isUsed: false - }; + ...path, + points: path.points.map((point) => { + if (point.uuid === selectedActionSphere.point.uuid) { + const triggerIndex = point.triggers.length; + const newTrigger = { + uuid: THREE.MathUtils.generateUUID(), + name: `Trigger ${triggerIndex + 1}`, + type: "", + bufferTime: 0, + isUsed: false, + }; - return { ...point, triggers: [...point.triggers, newTrigger] }; - } - return point; - }), - } + return { ...point, triggers: [...point.triggers, newTrigger] }; + } + return point; + }), + } : path ); @@ -262,13 +287,18 @@ const ConveyorMechanics: React.FC = () => { const updatedPaths = simulationPaths.map((path) => path.type === "Conveyor" ? { - ...path, - points: path.points.map((point) => - point.uuid === selectedActionSphere.point.uuid - ? { ...point, triggers: point.triggers.filter(trigger => trigger.uuid !== uuid) } - : point - ), - } + ...path, + points: path.points.map((point) => + point.uuid === selectedActionSphere.point.uuid + ? { + ...point, + triggers: point.triggers.filter( + (trigger) => trigger.uuid !== uuid + ), + } + : point + ), + } : path ); @@ -281,18 +311,20 @@ const ConveyorMechanics: React.FC = () => { const updatedPaths = simulationPaths.map((path) => path.type === "Conveyor" ? { - ...path, - points: path.points.map((point) => - point.uuid === selectedActionSphere.point.uuid - ? { - ...point, - triggers: point.triggers.map((trigger) => - trigger.uuid === uuid ? { ...trigger, type: triggerType } : trigger - ), - } - : point - ), - } + ...path, + points: path.points.map((point) => + point.uuid === selectedActionSphere.point.uuid + ? { + ...point, + triggers: point.triggers.map((trigger) => + trigger.uuid === uuid + ? { ...trigger, type: triggerType } + : trigger + ), + } + : point + ), + } : path ); @@ -309,26 +341,25 @@ const ConveyorMechanics: React.FC = () => { } }; - // Update the toggle handlers to immediately update the selected item const handleActionToggle = (uuid: string) => { if (!selectedActionSphere) return; const updatedPaths = simulationPaths.map((path) => path.type === "Conveyor" ? { - ...path, - points: path.points.map((point) => - point.uuid === selectedActionSphere.point.uuid - ? { - ...point, - actions: point.actions.map((action) => ({ - ...action, - isUsed: action.uuid === uuid ? !action.isUsed : false, - })), - } - : point - ), - } + ...path, + points: path.points.map((point) => + point.uuid === selectedActionSphere.point.uuid + ? { + ...point, + actions: point.actions.map((action) => ({ + ...action, + isUsed: action.uuid === uuid ? !action.isUsed : false, + })), + } + : point + ), + } : path ); @@ -340,8 +371,8 @@ const ConveyorMechanics: React.FC = () => { ...selectedItem, item: { ...selectedItem.item, - isUsed: !selectedItem.item.isUsed - } + isUsed: !selectedItem.item.isUsed, + }, }); } }; @@ -353,19 +384,19 @@ const ConveyorMechanics: React.FC = () => { const updatedPaths = simulationPaths.map((path) => path.type === "Conveyor" ? { - ...path, - points: path.points.map((point) => - point.uuid === selectedActionSphere.point.uuid - ? { - ...point, - triggers: point.triggers.map((trigger) => ({ - ...trigger, - isUsed: trigger.uuid === uuid ? !trigger.isUsed : false, - })), - } - : point - ), - } + ...path, + points: path.points.map((point) => + point.uuid === selectedActionSphere.point.uuid + ? { + ...point, + triggers: point.triggers.map((trigger) => ({ + ...trigger, + isUsed: trigger.uuid === uuid ? !trigger.isUsed : false, + })), + } + : point + ), + } : path ); @@ -377,8 +408,8 @@ const ConveyorMechanics: React.FC = () => { ...selectedItem, item: { ...selectedItem.item, - isUsed: !selectedItem.item.isUsed - } + isUsed: !selectedItem.item.isUsed, + }, }); } }; @@ -389,18 +420,20 @@ const ConveyorMechanics: React.FC = () => { const updatedPaths = simulationPaths.map((path) => path.type === "Conveyor" ? { - ...path, - points: path.points.map((point) => - point.uuid === selectedActionSphere.point.uuid - ? { - ...point, - triggers: point.triggers.map((trigger) => - trigger.uuid === uuid ? { ...trigger, bufferTime } : trigger - ), - } - : point - ), - } + ...path, + points: path.points.map((point) => + point.uuid === selectedActionSphere.point.uuid + ? { + ...point, + triggers: point.triggers.map((trigger) => + trigger.uuid === uuid + ? { ...trigger, bufferTime } + : trigger + ), + } + : point + ), + } : path ); @@ -412,13 +445,16 @@ const ConveyorMechanics: React.FC = () => { ...selectedItem, item: { ...selectedItem.item, - bufferTime - } + bufferTime, + }, }); } }; - const [selectedItem, setSelectedItem] = useState<{ type: "action" | "trigger"; item: any; } | null>(null); + const [selectedItem, setSelectedItem] = useState<{ + type: "action" | "trigger"; + item: any; + } | null>(null); useEffect(() => { setSelectedItem(null); @@ -426,21 +462,20 @@ const ConveyorMechanics: React.FC = () => { return (
- {!selectedPath && + {!selectedPath && (
{selectedActionSphere?.path?.modelName || "point name not found"}
- } - - {selectedPath && + )} + {selectedPath && (
{selectedPath.path.modelName || "path name not found"}
- } + )}
- {!selectedPath && + {!selectedPath && ( <>
@@ -458,16 +493,20 @@ const ConveyorMechanics: React.FC = () => { {selectedPoint?.actions.map((action) => (
setSelectedItem({ type: "action", item: action })} + onClick={() => + setSelectedItem({ type: "action", item: action }) + } > +
{ {selectedPoint?.triggers.map((trigger) => (
setSelectedItem({ type: "trigger", item: trigger })} + onClick={() => + setSelectedItem({ type: "trigger", item: trigger }) + } > +
{
- } + )}
{selectedItem && ( @@ -553,48 +596,69 @@ const ConveyorMechanics: React.FC = () => { handleActionSelect(selectedItem.item.uuid, option)} + onSelect={(option) => + handleActionSelect(selectedItem.item.uuid, option) + } /> {/* Only show material dropdown for Spawn/Swap actions */} - {(selectedItem.item.type === 'Spawn' || selectedItem.item.type === 'Swap') && ( + {(selectedItem.item.type === "Spawn" || + selectedItem.item.type === "Swap") && ( handleMaterialSelect(selectedItem.item.uuid, option)} + onSelect={(option) => + handleMaterialSelect(selectedItem.item.uuid, option) + } /> )} {/* Only show delay input for Delay actions */} - {selectedItem.item.type === 'Delay' && ( + {selectedItem.item.type === "Delay" && ( { const numValue = parseInt(value); handleDelayChange( selectedItem.item.uuid, - !value ? 'Inherit' : numValue + !value ? "Inherit" : numValue ); }} /> )} {/* Only show spawn interval for Spawn actions */} - {selectedItem.item.type === 'Spawn' && ( + {selectedItem.item.type === "Spawn" && ( { - handleSpawnIntervalChange(selectedItem.item.uuid, (value === "") ? "Inherit" : parseInt(value)); + handleSpawnIntervalChange( + selectedItem.item.uuid, + value === "" ? "Inherit" : parseInt(value) + ); }} /> - )} )} @@ -609,9 +673,13 @@ const ConveyorMechanics: React.FC = () => { /> handleTriggerSelect(selectedItem.item.uuid, option)} + onSelect={(option) => + handleTriggerSelect(selectedItem.item.uuid, option) + } /> {selectedItem.item.type === "Buffer" && ( @@ -619,23 +687,34 @@ const ConveyorMechanics: React.FC = () => { label="Buffer Time" value={selectedItem.item.bufferTime.toString()} onChange={(value) => { - handleTriggerBufferTimeChange(selectedItem.item.uuid, parseInt(value)); + handleTriggerBufferTimeChange( + selectedItem.item.uuid, + parseInt(value) + ); }} /> )} )} - )} {selectedPath && !selectedItem && ( -
+
handleSpeedChange((value === "") ? "Inherit" : parseInt(value))} + value={ + selectedPath.path.speed === "Inherit" + ? "" + : selectedPath.path.speed.toString() + } + onChange={(value) => + handleSpeedChange(value === "" ? "Inherit" : parseInt(value)) + } />
)} @@ -657,4 +736,4 @@ const ConveyorMechanics: React.FC = () => { ); }; -export default ConveyorMechanics; \ No newline at end of file +export default ConveyorMechanics; diff --git a/app/src/components/ui/menu/menu.tsx b/app/src/components/ui/menu/menu.tsx index b4ab78e..a416c2c 100644 --- a/app/src/components/ui/menu/menu.tsx +++ b/app/src/components/ui/menu/menu.tsx @@ -1,5 +1,6 @@ import React, { useState } from "react"; import { ArrowIcon } from "../../icons/ExportCommonIcons"; +import { toggleTheme } from "../../../utils/theme"; interface MenuBarProps { setOpenMenu: (isOpen: boolean) => void; // Function to update menu state @@ -22,6 +23,13 @@ const MenuBar: React.FC = ({ setOpenMenu }) => { })); }; + function handleThemeChange(){ + toggleTheme(); + window.location.reload(); + } + + const savedTheme: string | null = localStorage.getItem("theme") || "light"; + return (
= ({ setOpenMenu }) => {
Export as...
+
setActiveMenu("theme")} + onMouseLeave={() => { + setActiveMenu(null); + setActiveSubMenu(null); + }} + onClick={() => { + handleThemeChange(); + }} + > +
+ Theme
{savedTheme}
+
+
+ {/* Apps Menu */} {/*
{ - const [lines, setLines] = useState<{ distance: string; position: THREE.Vector3; userData: Types.Line; layer: string }[]>([]); - const { activeLayer } = useActiveLayer(); - const { toggleView } = useToggleView(); - const { newLines, setNewLines } = useNewLines(); - const { deletedLines, setDeletedLines } = useDeletedLines(); + const [lines, setLines] = useState< + { + distance: string; + position: THREE.Vector3; + userData: Types.Line; + layer: string; + }[] + >([]); + const { activeLayer } = useActiveLayer(); + const { toggleView } = useToggleView(); + const { newLines, setNewLines } = useNewLines(); + const { deletedLines, setDeletedLines } = useDeletedLines(); - useEffect(() => { - const email = localStorage.getItem('email') - if (!email) return; - const organization = (email.split("@")[1]).split(".")[0]; + useEffect(() => { + const email = localStorage.getItem("email"); + if (!email) return; + const organization = email.split("@")[1].split(".")[0]; - getLines(organization).then((data) => { - data = objectLinesToArray(data); + getLines(organization).then((data) => { + data = objectLinesToArray(data); - const lines = data.filter((line: Types.Line) => line[0][2] === activeLayer) - .map((line: Types.Line) => { - const point1 = new THREE.Vector3(line[0][0].x, line[0][0].y, line[0][0].z); - const point2 = new THREE.Vector3(line[1][0].x, line[1][0].y, line[1][0].z); - const distance = point1.distanceTo(point2); - const midpoint = new THREE.Vector3().addVectors(point1, point2).divideScalar(2); - return { - distance: distance.toFixed(1), - position: midpoint, - userData: line, - layer: activeLayer, - }; - }); - setLines(lines) - }) - }, [activeLayer]) + const lines = data + .filter((line: Types.Line) => line[0][2] === activeLayer) + .map((line: Types.Line) => { + const point1 = new THREE.Vector3( + line[0][0].x, + line[0][0].y, + line[0][0].z + ); + const point2 = new THREE.Vector3( + line[1][0].x, + line[1][0].y, + line[1][0].z + ); + const distance = point1.distanceTo(point2); + const midpoint = new THREE.Vector3() + .addVectors(point1, point2) + .divideScalar(2); + return { + distance: distance.toFixed(1), + position: midpoint, + userData: line, + layer: activeLayer, + }; + }); + setLines(lines); + }); + }, [activeLayer]); - useEffect(() => { - if (newLines.length > 0) { - if (newLines[0][0][2] !== activeLayer) return; - const newLinesData = newLines.map((line: Types.Line) => { - const point1 = new THREE.Vector3(line[0][0].x, line[0][0].y, line[0][0].z); - const point2 = new THREE.Vector3(line[1][0].x, line[1][0].y, line[1][0].z); - const distance = point1.distanceTo(point2); - const midpoint = new THREE.Vector3().addVectors(point1, point2).divideScalar(2); + useEffect(() => { + if (newLines.length > 0) { + if (newLines[0][0][2] !== activeLayer) return; + const newLinesData = newLines.map((line: Types.Line) => { + const point1 = new THREE.Vector3( + line[0][0].x, + line[0][0].y, + line[0][0].z + ); + const point2 = new THREE.Vector3( + line[1][0].x, + line[1][0].y, + line[1][0].z + ); + const distance = point1.distanceTo(point2); + const midpoint = new THREE.Vector3() + .addVectors(point1, point2) + .divideScalar(2); - return { - distance: distance.toFixed(1), - position: midpoint, - userData: line, - layer: activeLayer, - }; - }); - setLines((prevLines) => [...prevLines, ...newLinesData]); - setNewLines([]); - } - }, [newLines, activeLayer]); + return { + distance: distance.toFixed(1), + position: midpoint, + userData: line, + layer: activeLayer, + }; + }); + setLines((prevLines) => [...prevLines, ...newLinesData]); + setNewLines([]); + } + }, [newLines, activeLayer]); + useEffect(() => { + if ((deletedLines as Types.Lines).length > 0) { + setLines((prevLines) => + prevLines.filter( + (line) => + !deletedLines.some( + (deletedLine: any) => + deletedLine[0][1] === line.userData[0][1] && + deletedLine[1][1] === line.userData[1][1] + ) + ) + ); + setDeletedLines([]); + } + }, [deletedLines]); - useEffect(() => { - if ((deletedLines as Types.Lines).length > 0) { - setLines((prevLines) => - prevLines.filter( - (line) => !deletedLines.some((deletedLine: any) => deletedLine[0][1] === line.userData[0][1] && deletedLine[1][1] === line.userData[1][1]) - ) - ); - setDeletedLines([]); - } - }, [deletedLines]); + return ( + <> + {toggleView && ( + + {lines.map((text) => ( + +
+ {text.distance} m +
+ + ))} +
+ )} + + ); +}; - return ( - <> - {toggleView && ( - - {lines.map((text) => ( - -
{text.distance} m
- - ))} -
- )} - - ) - -} - -export default DistanceText; \ No newline at end of file +export default DistanceText; diff --git a/app/src/modules/builder/groups/zoneGroup.tsx b/app/src/modules/builder/groups/zoneGroup.tsx index 3d47a7e..c5e54d3 100644 --- a/app/src/modules/builder/groups/zoneGroup.tsx +++ b/app/src/modules/builder/groups/zoneGroup.tsx @@ -434,9 +434,9 @@ const ZoneGroup: React.FC = () => { const point2 = new THREE.Vector3(nextPoint[0], nextPoint[1], nextPoint[2]); const planeWidth = point1.distanceTo(point2); - const planeHeight = CONSTANTS.wallConfig.height; + const planeHeight = CONSTANTS.zoneConfig.height; - const midpoint = new THREE.Vector3((point1.x + point2.x) / 2, (CONSTANTS.wallConfig.height / 2) + ((zone.layer - 1) * CONSTANTS.wallConfig.height), (point1.z + point2.z) / 2); + const midpoint = new THREE.Vector3((point1.x + point2.x) / 2, (CONSTANTS.zoneConfig.height / 2) + ((zone.layer - 1) * CONSTANTS.zoneConfig.height), (point1.z + point2.z) / 2); const angle = Math.atan2(point2.z - point1.z, point2.x - point1.x); diff --git a/app/src/modules/scene/environment/ground.tsx b/app/src/modules/scene/environment/ground.tsx index 6a54577..ebc017e 100644 --- a/app/src/modules/scene/environment/ground.tsx +++ b/app/src/modules/scene/environment/ground.tsx @@ -1,9 +1,9 @@ -import * as THREE from 'three'; import { useToggleView } from '../../../store/store'; import * as CONSTANTS from '../../../types/world/worldConstants'; const Ground = ({ grid, plane }: any) => { - const { toggleView, setToggleView } = useToggleView(); + const { toggleView } = useToggleView(); + const savedTheme: string | null = localStorage.getItem('theme'); return ( @@ -19,4 +19,4 @@ const Ground = ({ grid, plane }: any) => { ) } -export default Ground; \ No newline at end of file +export default Ground; diff --git a/app/src/modules/scene/scene.tsx b/app/src/modules/scene/scene.tsx index 74693b4..0a01a99 100644 --- a/app/src/modules/scene/scene.tsx +++ b/app/src/modules/scene/scene.tsx @@ -1,11 +1,11 @@ -import { useMemo, useState } from "react"; +import { useMemo } from "react"; import { Canvas } from "@react-three/fiber"; import { Environment, KeyboardControls } from "@react-three/drei"; import World from "./world/world"; import Controls from "./controls/controls"; import TransformControl from "./controls/transformControls"; -import PostProcessing from "./postProcessing/postProcessing" +import PostProcessing from "./postProcessing/postProcessing"; import Sun from "./environment/sky"; import CamModelsGroup from "../collaboration/collabCams"; import Shadows from "./environment/shadow"; @@ -15,33 +15,31 @@ import background from "../../assets/textures/hdr/mudroadpuresky2k.hdr"; import SelectionControls from "./controls/selection/selectionControls"; import MeasurementTool from "./tools/measurementTool"; import Simulation from "../simulation/simulation"; -import DroppedObjects from "../../components/ui/componets/DroppedFloatingWidgets"; // import Simulation from "./simulationtemp/simulation"; import ZoneCentreTarget from "../../components/ui/componets/zoneCameraTarget"; -import ProductionCapacity from "../../components/layout/3D-cards/cards/ProductionCapacity"; import Dropped3dWidgets from "../../components/ui/componets/Dropped3dWidget"; -import { useWidgetSubOption } from "../../store/store"; export default function Scene() { - - const map = useMemo(() => [ - { name: "forward", keys: ["ArrowUp", "w", "W"] }, - { name: "backward", keys: ["ArrowDown", "s", "S"] }, - { name: "left", keys: ["ArrowLeft", "a", "A"] }, - { name: "right", keys: ["ArrowRight", "d", "D"] }, - ], []) + const map = useMemo( + () => [ + { name: "forward", keys: ["ArrowUp", "w", "W"] }, + { name: "backward", keys: ["ArrowDown", "s", "S"] }, + { name: "left", keys: ["ArrowLeft", "a", "A"] }, + { name: "right", keys: ["ArrowRight", "d", "D"] }, + ], + [] + ); + const savedTheme: string | null = localStorage.getItem("theme"); return ( { e.preventDefault(); }} - > @@ -52,7 +50,7 @@ export default function Scene() { - + {savedTheme !== "dark" && } diff --git a/app/src/styles/abstracts/variables.scss b/app/src/styles/abstracts/variables.scss index 4d201ad..44e5627 100644 --- a/app/src/styles/abstracts/variables.scss +++ b/app/src/styles/abstracts/variables.scss @@ -27,7 +27,7 @@ $input-text-color-dark: #b5b5c8; // Input field text color for dark mode // Accent colors $accent-color: #6f42c1; // Primary accent color -$accent-color-dark: #b392f0; // Primary accent color for dark mode +$accent-color-dark: #c4abf1; // Primary accent color for dark mode $highlight-accent-color: #e0dfff; // Highlighted accent for light mode $highlight-accent-color-dark: #403e6a; // Highlighted accent for dark mode @@ -45,6 +45,7 @@ $border-color-dark: #403e6a; // Border color for dark mode // Shadow color $shadow-color: #3c3c431a; // Shadow base color for light and dark mode +$shadow-color-dark: #8f8f8f1a; // Shadow base color for light and dark mode // Gradients $acent-gradient-dark: linear-gradient( diff --git a/app/src/styles/base/base.scss b/app/src/styles/base/base.scss index 86495f5..6adc1c5 100644 --- a/app/src/styles/base/base.scss +++ b/app/src/styles/base/base.scss @@ -58,7 +58,7 @@ --border-color: #{$border-color-dark}; // Border color for dark theme // Shadow variables - --shadow-main-dark: #{$shadow-color}; // Main shadow color + --shadow-main-dark: #{$shadow-color-dark}; // Main shadow color --box-shadow-light: 0px 2px 4px var(--shadow-main-dark); // Light shadow --box-shadow-medium: 0px 4px 8px var(--shadow-main-dark); // Medium shadow --box-shadow-heavy: 0px 8px 16px var(--shadow-main-dark); // Heavy shadow diff --git a/app/src/styles/components/input.scss b/app/src/styles/components/input.scss index e5dd2b4..9f6ee48 100644 --- a/app/src/styles/components/input.scss +++ b/app/src/styles/components/input.scss @@ -1,6 +1,22 @@ @use "../abstracts/variables" as *; @use "../abstracts/mixins" as *; +// global input style + +input { + width: 100%; + padding: 2px 4px; + border-radius: #{$border-radius-small}; + border: 1px solid var(--border-color); + outline: none; + background: transparent; + + &:focus, + &:active { + border: 1px solid var(--accent-color); + } +} + .input-value { color: var(--input-text-color); font-size: var(--font-size-regular); @@ -169,7 +185,6 @@ display: flex; justify-content: space-between; cursor: pointer; - border: 1px solid var(--primary-color); border-radius: 6px; background-color: var(--background-color); } @@ -214,8 +229,8 @@ position: relative; .dropdown { - top: 3px; - right: 3px; + top: 2px; + right: 2px; position: absolute; background: var(--highlight-accent-color); border-radius: #{$border-radius-small}; @@ -228,19 +243,6 @@ } } -input { - width: 100%; - padding: 2px 4px; - border-radius: #{$border-radius-small}; - border: 1px solid var(--border-color); - outline: none; - - &:focus, - &:active { - border: 1px solid var(--accent-color); - } -} - .eye-dropper-input-container { display: flex; align-items: center; @@ -607,6 +609,7 @@ input { input { border: none; + background: transparent; &::placeholder { color: var(--text-disabled); diff --git a/app/src/styles/components/menu/menu.scss b/app/src/styles/components/menu/menu.scss index 8635b0d..55b16f9 100644 --- a/app/src/styles/components/menu/menu.scss +++ b/app/src/styles/components/menu/menu.scss @@ -96,7 +96,10 @@ } &:hover { background-color: var(--highlight-accent-color); - color: var(--highlight-accent-color); + span, + .menu-item-right span { + color: var(--accent-color); + } } .menu-item-right { @@ -138,7 +141,9 @@ color: var(--text-color); &:hover { background-color: var(--highlight-accent-color); - color: var(--highlight-accent-color); + span { + color: var(--accent-color); + } } .shortcut { color: var(--text-color); @@ -150,7 +155,9 @@ &:hover { background-color: var(--highlight-accent-color); - color: var(--highlight-accent-color); + .menu-button { + color: var(--accent-color); + } } } } diff --git a/app/src/styles/layout/sidebar.scss b/app/src/styles/layout/sidebar.scss index 1e2de3c..e45dc20 100644 --- a/app/src/styles/layout/sidebar.scss +++ b/app/src/styles/layout/sidebar.scss @@ -253,6 +253,10 @@ .user-profile-container { display: flex; + .user-profile{ + background: var(--accent-color); + color: var(--primary-color); + } .user-organization { height: 26px; @@ -665,6 +669,10 @@ font-weight: var(--font-weight-regular); padding: 8px 0; } + .input-toggle-container{ + padding: 0; + margin-bottom: 6px; + } .value-field-container { margin-bottom: 6px; diff --git a/app/src/styles/main.scss b/app/src/styles/main.scss index 9d4bde4..7ceb752 100644 --- a/app/src/styles/main.scss +++ b/app/src/styles/main.scss @@ -32,7 +32,10 @@ @use 'layout/toast'; // pages -@use 'pages/dashboard.scss'; +@use 'pages/dashboard'; @use 'pages/home'; @use 'pages/realTimeViz'; -@use 'pages/userAuth'; \ No newline at end of file +@use 'pages/userAuth'; + +// +@use './scene/scene' \ No newline at end of file diff --git a/app/src/styles/pages/realTimeViz.scss b/app/src/styles/pages/realTimeViz.scss index 69df460..aa9e1aa 100644 --- a/app/src/styles/pages/realTimeViz.scss +++ b/app/src/styles/pages/realTimeViz.scss @@ -22,7 +22,7 @@ min-height: 83px; background: var(--background-color); border: 1.23px solid var(--border-color); - box-shadow: 0px 4.91px 4.91px 0px #0000001c; + box-shadow: var(--box-shadow-heavy); border-radius: $border-radius-medium; padding: 18px; position: absolute; @@ -31,6 +31,7 @@ .scene-container { overflow: hidden; + background: #232323; } .icon { diff --git a/app/src/styles/scene/scene.scss b/app/src/styles/scene/scene.scss new file mode 100644 index 0000000..9de35db --- /dev/null +++ b/app/src/styles/scene/scene.scss @@ -0,0 +1,23 @@ +@use "../abstracts/variables" as *; +@use "../abstracts/mixins" as *; + +.distance-text-wrapper { + pointer-events: none !important; +} +.distance-text { + pointer-events: none !important; + .distance { + position: absolute; + transform: translate(-50%, -50%) scale(.8); + pointer-events: none !important; + white-space: nowrap; + // style + font-size: var(--font-size-large); + padding: 2px 8px; + background: var(--primary-color); + color: var(--accent-color); + outline: 1px solid var(--accent-color); + border-radius: #{$border-radius-medium}; + box-shadow: var(--box-shadow-light); + } +} diff --git a/app/src/types/world/worldConstants.ts b/app/src/types/world/worldConstants.ts index b14e799..0a62e9d 100644 --- a/app/src/types/world/worldConstants.ts +++ b/app/src/types/world/worldConstants.ts @@ -1,3 +1,5 @@ +const savedTheme: string | null = localStorage.getItem("theme"); + export type Controls = { azimuthRotateSpeed: number; polarRotateSpeed: number; @@ -62,7 +64,6 @@ export type GridConfig = { divisions: number; primaryColor: string; secondaryColor: string; - position2D: [x: number, y: number, z: number]; position3D: [x: number, y: number, z: number]; } @@ -71,7 +72,6 @@ export type PlaneConfig = { position2D: [x: number, y: number, z: number]; position3D: [x: number, y: number, z: number]; rotation: number; - width: number; height: number; color: string; @@ -79,7 +79,6 @@ export type PlaneConfig = { export type ShadowConfig = { shadowOffset: number, - shadowmapSizewidth: number, shadowmapSizeheight: number, shadowcamerafar: number, @@ -90,10 +89,8 @@ export type ShadowConfig = { shadowcameraright: number, shadowbias: number, shadownormalBias: number, - shadowMaterialPosition: [x: number, y: number, z: number], shadowMaterialRotation: [x: number, y: number, z: number], - shadowMaterialOpacity: number, } @@ -117,12 +114,10 @@ export type PointConfig = { defaultOuterColor: string; deleteColor: string; boxScale: [number, number, number]; - wallOuterColor: string; floorOuterColor: string; aisleOuterColor: string; zoneOuterColor: string; - snappingThreshold: number; } @@ -130,17 +125,13 @@ export type LineConfig = { tubularSegments: number; radius: number; radialSegments: number; - wallName: string; floorName: string; aisleName: string; zoneName: string; referenceName: string; - lineIntersectionPoints: number; - defaultColor: string; - wallColor: string; floorColor: string; aisleColor: string; @@ -157,7 +148,6 @@ export type WallConfig = { export type FloorConfig = { defaultColor: string; height: number; - textureScale: number; } @@ -169,13 +159,12 @@ export type RoofConfig = { export type AisleConfig = { width: number; height: number; - defaultColor: number; } export type ZoneConfig = { defaultColor: string; - + height: number; color: string; } @@ -256,8 +245,8 @@ export const camPositionUpdateInterval: number = 200; // Interval for updating t export const gridConfig: GridConfig = { size: 300, // Size of the grid divisions: 75, // Number of divisions in the grid - primaryColor: "#d5d5d5", // Primary color of the grid - secondaryColor: "#e3e3e3", // Secondary color of the grid + primaryColor: savedTheme === "dark" ? "#131313" : "#d5d5d5", // Primary color of the grid + secondaryColor: savedTheme === "dark" ? "#434343" : "#e3e3e3", // Secondary color of the grid position2D: [0, 0.1, 0], // Position of the grid in 2D view position3D: [0, -0.5, 0], // Position of the grid in 3D view @@ -270,7 +259,7 @@ export const planeConfig: PlaneConfig = { width: 300, // Width of the plane height: 300, // Height of the plane - color: "#f3f3f3" // Color of the plane + color: savedTheme === "dark" ? "#323232" : "#f3f3f3" // Color of the plane } export const shadowConfig: ShadowConfig = { @@ -371,7 +360,8 @@ export const aisleConfig: AisleConfig = { export const zoneConfig: ZoneConfig = { defaultColor: "black", // Default color of the zones - color: "blue" // Color of the zones + height: 3, + color: "#8656DF" // Color of the zones } export const columnConfig: ColumnConfig = { diff --git a/app/src/utils/theme.ts b/app/src/utils/theme.ts index 28f2488..03d2454 100644 --- a/app/src/utils/theme.ts +++ b/app/src/utils/theme.ts @@ -1,34 +1,31 @@ -export {}; +export { }; + // Function to set the theme based on user preference or system default function setTheme() { - // Check for saved theme in localStorage const savedTheme: string | null = localStorage.getItem('theme'); - - // If no saved theme, use system default preference const systemPrefersDark: boolean = window.matchMedia('(prefers-color-scheme: dark)').matches; const defaultTheme: string = savedTheme || (systemPrefersDark ? 'dark' : 'light'); - - // Set the theme on page load document.documentElement.setAttribute('data-theme', defaultTheme); + localStorage.setItem('theme', defaultTheme); } -// Call the function to set the theme +// Function to toggle the theme +export function toggleTheme() { + const currentTheme: string | null = document.documentElement.getAttribute('data-theme'); + const newTheme: string = currentTheme === 'dark' ? 'light' : 'dark'; + + document.documentElement.setAttribute('data-theme', newTheme); + localStorage.setItem('theme', newTheme); +} + +// Initialize theme on page load setTheme(); -// Check if the toggle button exists -const toggleSwitch: Element | null = document.querySelector('.theme-switch'); +// Example: Call toggleTheme() when a button is clicked +const toggleSwitch: Element | null = document.querySelector('#theme-switch'); if (toggleSwitch) { - toggleSwitch.addEventListener('click', () => { - const currentTheme: string | null = document.documentElement.getAttribute('data-theme'); - - // Toggle between dark and light themes - const newTheme: string = currentTheme === 'dark' ? 'light' : 'dark'; - document.documentElement.setAttribute('data-theme', newTheme); - - // Save the new preference in localStorage - localStorage.setItem('theme', newTheme); - }); + toggleSwitch.addEventListener('click', toggleTheme); } else { console.warn("Theme switch button not found!"); } From 9611ad69cf7e45ed1d7f7a08e85b201fd833cb8b Mon Sep 17 00:00:00 2001 From: Nalvazhuthi Date: Mon, 31 Mar 2025 18:22:40 +0530 Subject: [PATCH 06/11] updated distanceLine and fix drag bug --- .../components/ui/componets/DistanceLines.tsx | 93 +++++ .../ui/componets/DroppedFloatingWidgets.tsx | 391 ++++++++++++------ .../componets/functions/determinePosition.ts | 14 +- .../functions/getActiveProperties.ts | 26 +- .../zoneData/useFloatingDataStore.ts | 8 + app/src/store/useDroppedObjectsStore.ts | 2 + app/src/styles/pages/realTimeViz.scss | 152 +++++++ 7 files changed, 535 insertions(+), 151 deletions(-) create mode 100644 app/src/components/ui/componets/DistanceLines.tsx create mode 100644 app/src/services/realTimeVisulization/zoneData/useFloatingDataStore.ts diff --git a/app/src/components/ui/componets/DistanceLines.tsx b/app/src/components/ui/componets/DistanceLines.tsx new file mode 100644 index 0000000..07cf202 --- /dev/null +++ b/app/src/components/ui/componets/DistanceLines.tsx @@ -0,0 +1,93 @@ +import React from "react"; + +interface DistanceLinesProps { + obj: { + position: { + top?: number | "auto"; + left?: number | "auto"; + right?: number | "auto"; + bottom?: number | "auto"; + }; + }; + activeEdges: { + vertical: "top" | "bottom"; + horizontal: "left" | "right"; + } | null; +} + +const DistanceLines: React.FC = ({ obj, activeEdges }) => { + if (!activeEdges) return null; + + return ( + <> + {activeEdges.vertical === "top" && typeof obj.position.top === "number" && ( +
+ {obj.position.top}px +
+ )} + + {activeEdges.vertical === "bottom" && + typeof obj.position.bottom === "number" && ( +
+ {obj.position.bottom}px +
+ )} + + {activeEdges.horizontal === "left" && + typeof obj.position.left === "number" && ( +
+ {obj.position.left}px +
+ )} + + {activeEdges.horizontal === "right" && + typeof obj.position.right === "number" && ( +
+ {obj.position.right}px +
+ )} + + ); +}; + +export default DistanceLines; \ 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 bf19bb9..272051c 100644 --- a/app/src/components/ui/componets/DroppedFloatingWidgets.tsx +++ b/app/src/components/ui/componets/DroppedFloatingWidgets.tsx @@ -1,6 +1,5 @@ import { WalletIcon } from "../../icons/3dChartIcons"; import { useEffect, useRef, useState } from "react"; -import { Line } from "react-chartjs-2"; import { useDroppedObjectsStore, Zones, @@ -9,29 +8,61 @@ import useModuleStore from "../../../store/useModuleStore"; import { determinePosition } from "./functions/determinePosition"; import { getActiveProperties } from "./functions/getActiveProperties"; import { addingFloatingWidgets } from "../../../services/realTimeVisulization/zoneData/addFloatingWidgets"; +import { + DublicateIcon, + KebabIcon, + DeleteIcon, +} from "../../icons/ExportCommonIcons"; +import DistanceLines from "./DistanceLines"; // Import the DistanceLines component + +interface DraggingState { + zone: string; + index: number; + initialPosition: { + top: number | "auto"; + left: number | "auto"; + right: number | "auto"; + bottom: number | "auto"; + }; +} const DroppedObjects: React.FC = () => { const zones = useDroppedObjectsStore((state) => state.zones); - + const [openKebabId, setOpenKebabId] = useState(null); const updateObjectPosition = useDroppedObjectsStore( (state) => state.updateObjectPosition ); - const [draggingIndex, setDraggingIndex] = useState<{ - zone: string; - index: number; - } | null>(null); + const [draggingIndex, setDraggingIndex] = useState( + null + ); const [offset, setOffset] = useState<[number, number] | null>(null); - const positionRef = useRef<[number, number] | null>(null); + const [activeEdges, setActiveEdges] = useState<{ + vertical: "top" | "bottom"; + horizontal: "left" | "right"; + } | null>(null); // State to track active edges for distance lines + const [currentPosition, setCurrentPosition] = useState<{ + top: number | "auto"; + left: number | "auto"; + right: number | "auto"; + bottom: number | "auto"; + } | null>(null); // State to track the current position during drag const animationRef = useRef(null); const { activeModule } = useModuleStore(); - // Get the first zone and its objects - const zoneEntries = Object.entries(zones); - if (zoneEntries.length === 0) return null; // No zone, nothing to render - const [zoneName, zone] = zoneEntries[0]; // Only render the first zone + // Clean up animation frame on unmount + useEffect(() => { + return () => { + if (animationRef.current) { + cancelAnimationFrame(animationRef.current); + } + }; + }, []); - // Handle pointer down event - function handlePointerDown(event: React.PointerEvent, index: number) { + const zoneEntries = Object.entries(zones); + if (zoneEntries.length === 0) return null; + const [zoneName, zone] = zoneEntries[0]; + + const handlePointerDown = (event: React.PointerEvent, index: number) => { const obj = zone.objects[index]; const container = document.getElementById("real-time-vis-canvas"); if (!container) return; @@ -40,41 +71,41 @@ const DroppedObjects: React.FC = () => { const relativeX = event.clientX - rect.left; const relativeY = event.clientY - rect.top; - // Determine which properties are active for this object + // Determine active properties for the initial position const [activeProp1, activeProp2] = getActiveProperties(obj.position); - // Calculate the offset based on the active properties + // Set active edges for distance lines + const vertical = activeProp1 === "top" ? "top" : "bottom"; + const horizontal = activeProp2 === "left" ? "left" : "right"; + setActiveEdges({ vertical, horizontal }); + + // Store the initial position strategy and active edges + setDraggingIndex({ + zone: zoneName, + index, + initialPosition: { ...obj.position }, + }); + + // Calculate offset from mouse to object edges let offsetX = 0; let offsetY = 0; if (activeProp1 === "top") { - offsetY = - relativeY - - (typeof obj.position.top === "number" ? obj.position.top : 0); - } else if (activeProp1 === "bottom") { - offsetY = - rect.height - - relativeY - - (typeof obj.position.bottom === "number" ? obj.position.bottom : 0); + offsetY = relativeY - (obj.position.top as number); + } else { + offsetY = rect.height - relativeY - (obj.position.bottom as number); } if (activeProp2 === "left") { - offsetX = - relativeX - - (typeof obj.position.left === "number" ? obj.position.left : 0); - } else if (activeProp2 === "right") { - offsetX = - rect.width - - relativeX - - (typeof obj.position.right === "number" ? obj.position.right : 0); + offsetX = relativeX - (obj.position.left as number); + } else { + offsetX = rect.width - relativeX - (obj.position.right as number); } - setDraggingIndex({ zone: zoneName, index }); setOffset([offsetY, offsetX]); - } + }; - // Handle pointer move event - function handlePointerMove(event: React.PointerEvent) { + const handlePointerMove = (event: React.PointerEvent) => { if (!draggingIndex || !offset) return; const container = document.getElementById("real-time-vis-canvas"); @@ -84,91 +115,194 @@ const DroppedObjects: React.FC = () => { const relativeX = event.clientX - rect.left; const relativeY = event.clientY - rect.top; - // Determine which properties are active for the dragged object - const obj = zone.objects[draggingIndex.index]; - const [activeProp1, activeProp2] = getActiveProperties(obj.position); + // Dynamically determine the current position strategy + const newPositionStrategy = determinePosition(rect, relativeX, relativeY); + const [activeProp1, activeProp2] = getActiveProperties(newPositionStrategy); - // Calculate the new position based on the active properties - let newX = 0; + // Update active edges for distance lines + const vertical = activeProp1 === "top" ? "top" : "bottom"; + const horizontal = activeProp2 === "left" ? "left" : "right"; + setActiveEdges({ vertical, horizontal }); + + // Calculate new position based on the active properties let newY = 0; - - if (activeProp2 === "left") { - newX = relativeX - offset[1]; - } else if (activeProp2 === "right") { - newX = rect.width - (relativeX + offset[1]); - } + let newX = 0; if (activeProp1 === "top") { newY = relativeY - offset[0]; - } else if (activeProp1 === "bottom") { + } else { newY = rect.height - (relativeY + offset[0]); } - // Ensure the object stays within the canvas boundaries + if (activeProp2 === "left") { + newX = relativeX - offset[1]; + } else { + newX = rect.width - (relativeX + offset[1]); + } + + // Apply boundaries newX = Math.max(0, Math.min(rect.width - 50, newX)); newY = Math.max(0, Math.min(rect.height - 50, newY)); - // Update the position reference - positionRef.current = [newY, newX]; + // Create new position object + const newPosition = { + ...newPositionStrategy, + [activeProp1]: newY, + [activeProp2]: newX, + // Clear opposite properties + [activeProp1 === "top" ? "bottom" : "top"]: "auto", + [activeProp2 === "left" ? "right" : "left"]: "auto", + }; + + // Update the current position state for DistanceLines + setCurrentPosition(newPosition); - // Update the object's position using requestAnimationFrame for smoother animations if (!animationRef.current) { animationRef.current = requestAnimationFrame(() => { - if (positionRef.current) { - updateObjectPosition(zoneName, draggingIndex.index, { - ...obj.position, - [activeProp1]: positionRef.current[0], - [activeProp2]: positionRef.current[1], - }); - } + updateObjectPosition(zoneName, draggingIndex.index, newPosition); animationRef.current = null; }); } - } + }; - // Handle pointer up event - async function handlePointerUp(event: React.MouseEvent) { + const handlePointerUp = async (event: React.PointerEvent) => { try { if (!draggingIndex || !offset) return; - const email = localStorage.getItem("email") || ""; - const organization = email?.split("@")[1]?.split(".")[0]; const container = document.getElementById("real-time-vis-canvas"); - if (!container) throw new Error("Canvas container not found"); + if (!container) return; const rect = container.getBoundingClientRect(); const relativeX = event.clientX - rect.left; const relativeY = event.clientY - rect.top; - // Recalculate the position using determinePosition - const newPosition = determinePosition(rect, relativeX, relativeY); + // Only now determine the final position strategy + const finalPosition = determinePosition(rect, relativeX, relativeY); + const [activeProp1, activeProp2] = getActiveProperties(finalPosition); - // Validate the dragging index and get the object - if (!zone.objects[draggingIndex.index]) { - throw new Error("Dragged object not found in the zone"); + // Calculate final position using the new strategy + let finalY = 0; + let finalX = 0; + + if (activeProp1 === "top") { + finalY = relativeY - offset[0]; + } else { + finalY = rect.height - (relativeY + offset[0]); } - const obj = { ...zone.objects[draggingIndex.index], position: newPosition }; - let response = await addingFloatingWidgets(zone.zoneId, organization, obj); + + if (activeProp2 === "left") { + finalX = relativeX - offset[1]; + } else { + finalX = rect.width - (relativeX + offset[1]); + } + + // Apply boundaries + finalX = Math.max(0, Math.min(rect.width - 50, finalX)); + finalY = Math.max(0, Math.min(rect.height - 50, finalY)); + + const boundedPosition = { + ...finalPosition, + [activeProp1]: finalY, + [activeProp2]: finalX, + }; + + // Save to backend + const email = localStorage.getItem("email") || ""; + const organization = email?.split("@")[1]?.split(".")[0]; + const response = await addingFloatingWidgets(zone.zoneId, organization, { + ...zone.objects[draggingIndex.index], + position: boundedPosition, + }); + if (response.message === "Widget updated successfully") { - updateObjectPosition(zoneName, draggingIndex.index, newPosition); + updateObjectPosition(zoneName, draggingIndex.index, boundedPosition); } - // Reset states + // Clean up setDraggingIndex(null); setOffset(null); - + setActiveEdges(null); // Clear active edges + setCurrentPosition(null); // Reset current position if (animationRef.current) { cancelAnimationFrame(animationRef.current); animationRef.current = null; } } catch (error) { - + console.error("Error in handlePointerUp:", error); } - } + }; + const handleKebabClick = (id: string, event: React.MouseEvent) => { + event.stopPropagation(); + setOpenKebabId((prevId) => (prevId === id ? null : id)); + }; + + const renderObjectContent = (obj: any) => { + switch (obj.className) { + case "floating total-card": + return ( + <> +
+
{obj.header}
+
+
{obj.value}
+
{obj.per}
+
+
+
+ +
+ + ); + case "warehouseThroughput floating": + return ( + <> +
+

Warehouse Throughput

+

+ (+5) more in 2025 +

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

Fleet Efficiency

+
+
+
+
+
+
+
+
+ 0% +
+
{obj.per}%
+
Optimal
+
+ 100% +
+ + ); + default: + return null; + } + }; return ( -
+
{zone.objects.map((obj, index) => (
{ style={{ position: "absolute", top: - typeof obj.position.top !== "string" + typeof obj.position.top === "number" ? `${obj.position.top}px` : "auto", left: - typeof obj.position.left !== "string" + typeof obj.position.left === "number" ? `${obj.position.left}px` : "auto", right: - typeof obj.position.right !== "string" + typeof obj.position.right === "number" ? `${obj.position.right}px` : "auto", bottom: - typeof obj.position.bottom !== "string" + typeof obj.position.bottom === "number" ? `${obj.position.bottom}px` : "auto", - // transition: draggingIndex?.index === index ? "none" : "transform 0.1s ease-out", }} onPointerDown={(event) => handlePointerDown(event, index)} > - {obj.className === "floating total-card" ? ( - <> -
-
{obj.header}
-
-
{obj.value}
-
{obj.per}
+ {renderObjectContent(obj)} +
handleKebabClick(obj.id, event)} + > + +
+ {openKebabId === obj.id && ( +
+
+
+
+
Duplicate
-
- -
- - ) : obj.className === "warehouseThroughput floating" ? ( - <> -
-

Warehouse Throughput

-

- (+5) more in 2025 -

-
-
- {/* */} -
- - ) : obj.className === "fleetEfficiency floating" ? ( - <> -

Fleet Efficiency

-
-
-
-
-
+
+
+
+
Delete
-
- 0% -
-
{obj.per}%
-
Optimal
-
- 100% -
- - ) : null} +
+ )}
))} + + {/* Render DistanceLines component during drag */} + {draggingIndex !== null && + activeEdges !== null && + currentPosition !== null && ( + + )}
); }; export default DroppedObjects; + +// in pointer move even when i goes to top right the value not changes to top right same problem in bottom left diff --git a/app/src/components/ui/componets/functions/determinePosition.ts b/app/src/components/ui/componets/functions/determinePosition.ts index 0fcd727..82f2303 100644 --- a/app/src/components/ui/componets/functions/determinePosition.ts +++ b/app/src/components/ui/componets/functions/determinePosition.ts @@ -8,11 +8,9 @@ export function determinePosition( right: number | "auto"; bottom: number | "auto"; } { - // Calculate the midpoints of the canvas const centerX = canvasRect.width / 2; const centerY = canvasRect.height / 2; - // Initialize position with default values let position: { top: number | "auto"; left: number | "auto"; @@ -21,9 +19,8 @@ export function determinePosition( }; if (relativeY < centerY) { - // Top half if (relativeX < centerX) { - // Left side + console.log("Top-left"); position = { top: relativeY, left: relativeX, @@ -31,7 +28,7 @@ export function determinePosition( bottom: "auto", }; } else { - // Right side + console.log("Top-right"); position = { top: relativeY, right: canvasRect.width - relativeX, @@ -40,9 +37,8 @@ export function determinePosition( }; } } else { - // Bottom half if (relativeX < centerX) { - // Left side + console.log("Bottom-left"); position = { bottom: canvasRect.height - relativeY, left: relativeX, @@ -50,7 +46,7 @@ export function determinePosition( top: "auto", }; } else { - // Right side + console.log("Bottom-right"); position = { bottom: canvasRect.height - relativeY, right: canvasRect.width - relativeX, @@ -61,4 +57,4 @@ export function determinePosition( } return position; -} +} \ No newline at end of file diff --git a/app/src/components/ui/componets/functions/getActiveProperties.ts b/app/src/components/ui/componets/functions/getActiveProperties.ts index 2cb0b1b..1a05d34 100644 --- a/app/src/components/ui/componets/functions/getActiveProperties.ts +++ b/app/src/components/ui/componets/functions/getActiveProperties.ts @@ -1,17 +1,11 @@ -export const getActiveProperties = (position: { - top: number | "auto"; - left: number | "auto"; - right: number | "auto"; - bottom: number | "auto"; -}) => { - let activeProps: ["top", "left"] | ["bottom", "right"] = ["top", "left"]; // Default to top-left - - if ( - typeof position.bottom !== "string" && - typeof position.right !== "string" - ) { - activeProps = ["bottom", "right"]; +export function getActiveProperties(position: any): [string, string] { + if (position.top !== "auto" && position.left !== "auto") { + return ["top", "left"]; // Top-left + } else if (position.top !== "auto" && position.right !== "auto") { + return ["top", "right"]; // Top-right + } else if (position.bottom !== "auto" && position.left !== "auto") { + return ["bottom", "left"]; // Bottom-left + } else { + return ["bottom", "right"]; // Bottom-right } - - return activeProps; -}; +} \ No newline at end of file diff --git a/app/src/services/realTimeVisulization/zoneData/useFloatingDataStore.ts b/app/src/services/realTimeVisulization/zoneData/useFloatingDataStore.ts new file mode 100644 index 0000000..39a542a --- /dev/null +++ b/app/src/services/realTimeVisulization/zoneData/useFloatingDataStore.ts @@ -0,0 +1,8 @@ +import { create } from "zustand"; + +const useFloatingDataStore = create((set) => ({ + floatingdata: [], // Initial state + setfloatingadata: (newData: []) => set({ floatingdata: newData }), // Setter function +})); + +export default useFloatingDataStore; diff --git a/app/src/store/useDroppedObjectsStore.ts b/app/src/store/useDroppedObjectsStore.ts index dc648a9..5ded701 100644 --- a/app/src/store/useDroppedObjectsStore.ts +++ b/app/src/store/useDroppedObjectsStore.ts @@ -90,3 +90,5 @@ export interface Zones { zoneId: string; objects: DroppedObject[]; } + + diff --git a/app/src/styles/pages/realTimeViz.scss b/app/src/styles/pages/realTimeViz.scss index 69df460..b080840 100644 --- a/app/src/styles/pages/realTimeViz.scss +++ b/app/src/styles/pages/realTimeViz.scss @@ -541,4 +541,156 @@ .zone.active { background-color: #007bff; color: white; +} + + + +.floating-wrapper { + .icon { + width: 25px !important; + height: 25px !important; + background-color: transparent; + } + + .kebab { + width: 30px; + height: 30px; + position: absolute !important; + top: 0px; + right: 0px; + z-index: 10; + cursor: pointer; + @include flex-center; + } + + .kebab-options { + position: absolute; + top: 18px; + right: 5px; + transform: translate(0px, 0); + background-color: var(--background-color); + z-index: 10; + + display: flex; + flex-direction: column; + gap: 6px; + border-radius: 4px; + + box-shadow: var(--box-shadow-medium); + + .btn { + display: flex; + gap: 6px; + align-items: center; + padding: 5px 10px; + color: var(--text-color); + cursor: pointer; + + &:hover { + .label { + color: var(--accent-color); + } + } + + &:hover { + background-color: var(--highlight-accent-color); + width: 100%; + + svg { + &:first-child { + fill: var(--accent-color); + } + + &:last-child { + fill: auto; + stroke: var(--accent-color); + } + } + } + } + + .dublicate { + cursor: not-allowed; + } + + + + } +} + + + + + + + + + + +/* General styles for all distance lines */ +.distance-line { + position: absolute; + border-style: dashed; + border-color: var(--accent-color); /* Green color for visibility */ + border-width: 1px; + pointer-events: none; /* Ensure lins don't interfere with dragging */ + z-index: 10000; +} + +/* Label styles for displaying distance values */ +.distance-label { + position: absolute; + background-color: var(--accent-color); + color: white; + font-size: 12px; + padding: 2px 6px; + border-radius: 3px; + white-space: nowrap; + transform: translate(-50%, -50%); /* Center the label */ +} + +/* Specific styles for each type of line */ + +/* Top distance line */ +.distance-line.top { + border-bottom: none; /* Remove bottom border for a single line */ + width: 2px; /* Thin vertical line */ +} + +.distance-line.top .distance-label { + top: -10px; /* Position label above the line */ + left: 50%; /* Center horizontally */ +} + +/* Bottom distance line */ +.distance-line.bottom { + border-top: none; /* Remove top border for a single line */ + width: 2px; /* Thin vertical line */ +} + +.distance-line.bottom .distance-label { + bottom: -10px; /* Position label below the line */ + left: 50%; /* Center horizontally */ +} + +/* Left distance line */ +.distance-line.left { + border-right: none; /* Remove right border for a single line */ + height: 2px; /* Thin horizontal line */ +} + +.distance-line.left .distance-label { + left: -10px; /* Position label to the left of the line */ + top: 50%; /* Center vertically */ +} + +/* Right distance line */ +.distance-line.right { + border-left: none; /* Remove left border for a single line */ + height: 2px; /* Thin horizontal line */ +} + +.distance-line.right .distance-label { + right: -10px; /* Position label to the right of the line */ + top: 50%; /* Center vertically */ } \ No newline at end of file From 6b8ccc02c740f9b1c857b5b867a81c5096fd8dfa Mon Sep 17 00:00:00 2001 From: Gomathi9520 Date: Mon, 31 Mar 2025 19:20:03 +0530 Subject: [PATCH 07/11] 3d widget api added and template frontend and backend completed --- .../sidebarLeft/visualization/Templates.tsx | 78 ++++++-- .../properties/ZoneProperties.tsx | 7 +- app/src/components/ui/Tools.tsx | 9 +- .../components/ui/componets/AddButtons.tsx | 1 - .../components/ui/componets/DisplayZone.tsx | 17 +- .../components/ui/componets/DistanceLine.tsx | 93 ++++++++++ .../ui/componets/Dropped3dWidget.tsx | 170 ++++++++++++------ .../ui/componets/DroppedFloatingWidgets.tsx | 52 +++++- .../ui/componets/RealTimeVisulization.tsx | 11 +- .../componets/functions/determinePosition.ts | 67 +++++++ .../ui/componets/handleDropTemplate .tsx | 29 +++ app/src/modules/simulation/simulation.tsx | 1 - .../visualization/handleSaveTemplate.ts | 61 ++++--- .../zoneData/add3dWidget.ts | 36 ++++ .../zoneData/addFloatingWidgets.ts | 1 + .../zoneData/deleteFloatingWidget.ts | 35 ++++ .../zoneData/deleteTemplate.ts | 32 ++++ .../zoneData/get3dWidgetData.ts | 25 +++ .../zoneData/getSelect2dZoneData.ts | 1 + .../zoneData/getTemplate.ts | 23 +++ .../realTimeVisulization/zoneData/getZones.ts | 2 +- .../zoneData/loadTemplate.ts | 33 ++++ .../zoneData/saveTempleteApi.ts | 28 +++ app/src/store/store.ts | 1 + app/src/store/useDroppedObjectsStore.ts | 77 ++++++++ app/src/store/useTemplateStore.ts | 18 +- app/src/styles/pages/realTimeViz.scss | 11 -- 27 files changed, 790 insertions(+), 129 deletions(-) create mode 100644 app/src/components/ui/componets/DistanceLine.tsx create mode 100644 app/src/components/ui/componets/handleDropTemplate .tsx create mode 100644 app/src/services/realTimeVisulization/zoneData/add3dWidget.ts create mode 100644 app/src/services/realTimeVisulization/zoneData/deleteFloatingWidget.ts create mode 100644 app/src/services/realTimeVisulization/zoneData/deleteTemplate.ts create mode 100644 app/src/services/realTimeVisulization/zoneData/get3dWidgetData.ts create mode 100644 app/src/services/realTimeVisulization/zoneData/getTemplate.ts create mode 100644 app/src/services/realTimeVisulization/zoneData/loadTemplate.ts create mode 100644 app/src/services/realTimeVisulization/zoneData/saveTempleteApi.ts diff --git a/app/src/components/layout/sidebarLeft/visualization/Templates.tsx b/app/src/components/layout/sidebarLeft/visualization/Templates.tsx index ef710f7..4b13429 100644 --- a/app/src/components/layout/sidebarLeft/visualization/Templates.tsx +++ b/app/src/components/layout/sidebarLeft/visualization/Templates.tsx @@ -1,25 +1,76 @@ +import { useEffect } from "react"; +import { useDroppedObjectsStore } from "../../../../store/useDroppedObjectsStore"; import useTemplateStore from "../../../../store/useTemplateStore"; import { useSelectedZoneStore } from "../../../../store/useZoneStore"; +import { getTemplateData } from "../../../../services/realTimeVisulization/zoneData/getTemplate"; +import { deleteTemplateApi } from "../../../../services/realTimeVisulization/zoneData/deleteTemplate"; +import { loadTempleteApi } from "../../../../services/realTimeVisulization/zoneData/loadTemplate"; const Templates = () => { const { templates, removeTemplate } = useTemplateStore(); - const { setSelectedZone } = useSelectedZoneStore(); + const { setTemplates } = useTemplateStore(); + const { setSelectedZone, selectedZone } = useSelectedZoneStore(); - const handleDeleteTemplate = (id: string) => { - removeTemplate(id); + useEffect(() => { + async function templateData() { + try { + const email = localStorage.getItem("email") || ""; + const organization = email?.split("@")[1]?.split(".")[0]; + let response = await getTemplateData(organization); + setTemplates(response); + } catch (error) { + console.error("Error fetching template data:", error); + } + } + + templateData(); + }, []); + + const handleDeleteTemplate = async (id: string) => { + try { + const email = localStorage.getItem("email") || ""; + const organization = email?.split("@")[1]?.split(".")[0]; + + let response = await deleteTemplateApi(id, organization); + + if (response.message === "Template deleted successfully") { + removeTemplate(id); + } + } catch (error) { + console.error("Error deleting template:", error); + } }; - const handleLoadTemplate = (template: any) => { - setSelectedZone((prev) => ({ - ...prev, - panelOrder: template.panelOrder, - activeSides: Array.from( - new Set([...prev.activeSides, ...template.panelOrder]) - ), - widgets: template.widgets, - })); + const handleLoadTemplate = async (template: any) => { + try { + if (selectedZone.zoneName === "") return; + + const email = localStorage.getItem("email") || ""; + const organization = email?.split("@")[1]?.split(".")[0]; + + let response = await loadTempleteApi(template.id, selectedZone.zoneId, organization); + + if (response.message === "Template placed in Zone") { + setSelectedZone({ + panelOrder: template.panelOrder, + activeSides: Array.from(new Set(template.panelOrder)), // No merging with previous `activeSides` + widgets: template.widgets, + }); + + useDroppedObjectsStore.getState().setZone(selectedZone.zoneName, selectedZone.zoneId); + + if (Array.isArray(template.floatingWidget)) { + template.floatingWidget.forEach((val: any) => { + useDroppedObjectsStore.getState().addObject(selectedZone.zoneName, val); + }); + } + } + } catch (error) { + console.error("Error loading template:", error); + } }; + return (
{ transition: "box-shadow 0.3s ease", }} > - {template.snapshot && ( + {template?.snapshot && (
{" "} {/* 16:9 aspect ratio */} @@ -122,3 +173,4 @@ const Templates = () => { }; export default Templates; + diff --git a/app/src/components/layout/sidebarRight/properties/ZoneProperties.tsx b/app/src/components/layout/sidebarRight/properties/ZoneProperties.tsx index 9234cc5..9e8b37e 100644 --- a/app/src/components/layout/sidebarRight/properties/ZoneProperties.tsx +++ b/app/src/components/layout/sidebarRight/properties/ZoneProperties.tsx @@ -20,17 +20,16 @@ const ZoneProperties: React.FC = () => { try { const email = localStorage.getItem("email") || ""; const organization = email?.split("@")[1]?.split(".")[0]; - + let zonesdata = { zoneId: selectedZone.zoneId, viewPortposition: zonePosition, viewPortCenter: zoneTarget }; - + let response = await zoneCameraUpdate(zonesdata, organization); console.log('response: ', response); - - + setEdit(false); } catch (error) { console.error("Error in handleSetView:", error); diff --git a/app/src/components/ui/Tools.tsx b/app/src/components/ui/Tools.tsx index c86164c..ba1f6e8 100644 --- a/app/src/components/ui/Tools.tsx +++ b/app/src/components/ui/Tools.tsx @@ -32,6 +32,7 @@ import { useTransformMode, } from "../../store/store"; import useToggleStore from "../../store/useUIToggleStore"; +import { use3DWidget, useFloatingWidget } from "../../store/useDroppedObjectsStore"; const Tools: React.FC = () => { const { templates } = useTemplateStore(); @@ -46,6 +47,8 @@ const Tools: React.FC = () => { const { isPlaying, setIsPlaying } = usePlayButtonStore(); const { addTemplate } = useTemplateStore(); const { selectedZone } = useSelectedZoneStore(); + const { floatingWidget } = useFloatingWidget() + const { widgets3D } = use3DWidget() // wall options const { toggleView, setToggleView } = useToggleView(); @@ -378,13 +381,17 @@ const Tools: React.FC = () => {
+ onClick={() => { + handleSaveTemplate({ addTemplate, + floatingWidget, + widgets3D, selectedZone, templates, }) } + } >
diff --git a/app/src/components/ui/componets/AddButtons.tsx b/app/src/components/ui/componets/AddButtons.tsx index 4f3f9b7..e0ca342 100644 --- a/app/src/components/ui/componets/AddButtons.tsx +++ b/app/src/components/ui/componets/AddButtons.tsx @@ -119,7 +119,6 @@ const AddButtons: React.FC = ({ }; // Delete the selectedZone state - setSelectedZone(updatedZone); } else { const updatePanelData = async () => { diff --git a/app/src/components/ui/componets/DisplayZone.tsx b/app/src/components/ui/componets/DisplayZone.tsx index 7689e51..3889139 100644 --- a/app/src/components/ui/componets/DisplayZone.tsx +++ b/app/src/components/ui/componets/DisplayZone.tsx @@ -2,9 +2,10 @@ import React, { useEffect, useRef, useState, useCallback } from "react"; import { Widget } from "../../../store/useWidgetStore"; import { MoveArrowLeft, MoveArrowRight } from "../../icons/SimulationIcons"; import { InfoIcon } from "../../icons/ExportCommonIcons"; -import { useDroppedObjectsStore } from "../../../store/useDroppedObjectsStore"; +import { useDroppedObjectsStore, useFloatingWidget } from "../../../store/useDroppedObjectsStore"; import { getSelect2dZoneData } from "../../../services/realTimeVisulization/zoneData/getSelect2dZoneData"; import { getFloatingZoneData } from "../../../services/realTimeVisulization/zoneData/getFloatingData"; +import { get3dWidgetZoneData } from "../../../services/realTimeVisulization/zoneData/get3dWidgetData"; // Define the type for `Side` type Side = "top" | "bottom" | "left" | "right"; @@ -72,6 +73,7 @@ const DisplayZone: React.FC = ({ // State to track overflow visibility const [showLeftArrow, setShowLeftArrow] = useState(false); const [showRightArrow, setShowRightArrow] = useState(false); + const { floatingWidget, setFloatingWidget } = useFloatingWidget() // Function to calculate overflow state const updateOverflowState = useCallback(() => { @@ -150,14 +152,16 @@ const DisplayZone: React.FC = ({ async function handleSelect2dZoneData(zoneId: string, zoneName: string) { try { if (selectedZone?.zoneId === zoneId) { - console.log("Zone is already selected:", zoneName); + return; } const email = localStorage.getItem("email") || ""; const organization = email?.split("@")[1]?.split(".")[0]; // Fetch data from backend let response = await getSelect2dZoneData(zoneId, organization); + console.log('response: ', response); let res = await getFloatingZoneData(zoneId, organization); + setFloatingWidget(res) // Set the selected zone in the store useDroppedObjectsStore.getState().setZone(zoneName, zoneId); if (Array.isArray(res)) { @@ -177,8 +181,8 @@ const DisplayZone: React.FC = ({ zoneViewPortPosition: response.viewPortposition || {}, }); } catch (error) { - console.log('error: ', error); - + + } } @@ -186,9 +190,8 @@ const DisplayZone: React.FC = ({ return (
{/* Left Arrow */} {showLeftArrow && ( diff --git a/app/src/components/ui/componets/DistanceLine.tsx b/app/src/components/ui/componets/DistanceLine.tsx new file mode 100644 index 0000000..8dec7b1 --- /dev/null +++ b/app/src/components/ui/componets/DistanceLine.tsx @@ -0,0 +1,93 @@ +import React from "react"; + +interface DistanceLinesProps { + obj: { + position: { + top?: number | "auto"; + left?: number | "auto"; + right?: number | "auto"; + bottom?: number | "auto"; + }; + }; + activeEdges: { + vertical: "top" | "bottom"; + horizontal: "left" | "right"; + } | null; +} + +const DistanceLines: React.FC = ({ obj, activeEdges }) => { + if (!activeEdges) return null; + + return ( + <> + {activeEdges.vertical === "top" && typeof obj.position.top === "number" && ( +
+ {obj.position.top.toFixed()}px +
+ )} + + {activeEdges.vertical === "bottom" && + typeof obj.position.bottom === "number" && ( +
+ {obj.position.bottom.toFixed()}px +
+ )} + + {activeEdges.horizontal === "left" && + typeof obj.position.left === "number" && ( +
+ {obj.position.left.toFixed()}px +
+ )} + + {activeEdges.horizontal === "right" && + typeof obj.position.right === "number" && ( +
+ {obj.position.right.toFixed()}px +
+ )} + + ); +}; + +export default DistanceLines; \ No newline at end of file diff --git a/app/src/components/ui/componets/Dropped3dWidget.tsx b/app/src/components/ui/componets/Dropped3dWidget.tsx index 10dd343..c627af4 100644 --- a/app/src/components/ui/componets/Dropped3dWidget.tsx +++ b/app/src/components/ui/componets/Dropped3dWidget.tsx @@ -10,79 +10,139 @@ import ProductionCapacity from "../../layout/3D-cards/cards/ProductionCapacity"; import ReturnOfInvestment from "../../layout/3D-cards/cards/ReturnOfInvestment"; import StateWorking from "../../layout/3D-cards/cards/StateWorking"; import { useSelectedZoneStore } from "../../../store/useZoneStore"; +import { generateUniqueId } from "../../../functions/generateUniqueId"; +import { adding3dWidgets } from "../../../services/realTimeVisulization/zoneData/add3dWidget"; +import { get3dWidgetZoneData } from "../../../services/realTimeVisulization/zoneData/get3dWidgetData"; +import { use3DWidget } from "../../../store/useDroppedObjectsStore"; export default function Dropped3dWidgets() { - const { widgetSelect, setWidgetSelect } = useAsset3dWidget(); + const { widgetSelect } = useAsset3dWidget(); const { activeModule } = useModuleStore(); const { raycaster, gl, scene }: ThreeState = useThree(); - const { selectedZone } = useSelectedZoneStore(); // Get currently selected zone - const { widgetSubOption, setWidgetSubOption } = useWidgetSubOption() - // 🔥 Store widget positions per zone - const [zoneWidgets, setZoneWidgets] = useState // Widget type -> Positions array - >>({}); - + const { widgetSubOption, setWidgetSubOption } = useWidgetSubOption(); + const { selectedZone } = useSelectedZoneStore(); // Get the currently active zone + // 🔥 Store widget data (id, type, position) based on the selected zone + const [zoneWidgetData, setZoneWidgetData] = useState< + Record + >({}); + const { setWidgets3D } = use3DWidget() useEffect(() => { - if (widgetSubOption === "Floating") return - // if (activeModule !== "visualization") return; - const canvasElement = gl.domElement; - const onDrop = (event: DragEvent) => { - event.preventDefault(); // Prevent default browser behavior - if (widgetSubOption === "3D") { - if (selectedZone.zoneName === "") return - if (!widgetSelect?.startsWith("ui")) return; - const group1 = scene.getObjectByName("itemsGroup"); - if (!group1) return; - const Assets = group1.children - .map((val) => scene.getObjectByProperty("uuid", val.uuid)) - .filter(Boolean) as THREE.Object3D[]; - 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; + if (activeModule !== "visualization") return + if (selectedZone.zoneName === "") return; - setZoneWidgets((prev) => ({ - ...prev, - [selectedZone.zoneId]: { - ...(prev[selectedZone.zoneId] || {}), - [widgetSelect]: [...(prev[selectedZone.zoneId]?.[widgetSelect] || []), [x, y, z]], - }, - })); - } + const email = localStorage.getItem("email") || ""; + const organization = email?.split("@")[1]?.split(".")[0]; + + async function get3dWidgetData() { + let result = await get3dWidgetZoneData(selectedZone.zoneId, organization); + setWidgets3D(result) + // Ensure the extracted data has id, type, and position correctly mapped + const formattedWidgets = result.map((widget: any) => ({ + id: widget.id, + type: widget.type, + position: widget.position, + })); + + setZoneWidgetData((prev) => ({ + ...prev, + [selectedZone.zoneId]: formattedWidgets, + })); + } + + get3dWidgetData(); + + }, [selectedZone.zoneId,activeModule]); + // useEffect(() => { + // // ✅ Set data only for the selected zone, keeping existing state structure + // setZoneWidgetData((prev) => ({ + // ...prev, + // [selectedZone.zoneId]: [ + // { + // "id": "1743322674626-50mucpb1c", + // "type": "ui-Widget 1", + // "position": [120.94655021768133, 4.142360029666558, 124.39283546121099] + // }, + // { + // "id": "1743322682086-je2h9x33v", + // "type": "ui-Widget 2", + // "position": [131.28751045879255, 0.009999999999970264, 133.92059801984362] + // } + // ] + // })); + // }, [selectedZone.zoneId]); // ✅ Only update when the zone changes + + useEffect(() => { + if (activeModule !== "visualization") return; + if (widgetSubOption === "Floating") return; + if (selectedZone.zoneName === "") return + const canvasElement = gl.domElement; + const onDrop = async (event: DragEvent) => { + event.preventDefault(); // Prevent default browser behavior + const email = localStorage.getItem("email") || ""; + const organization = email?.split("@")[1]?.split(".")[0]; + if (!widgetSelect.startsWith("ui")) return; + const group1 = scene.getObjectByName("itemsGroup"); + if (!group1) return; + const intersects = raycaster.intersectObjects(scene.children, true).filter( + (intersect) => + !intersect.object.name.includes("Roof") && + !intersect.object.name.includes("agv-collider") && + !intersect.object.name.includes("MeasurementReference") && + !intersect.object.userData.isPathObject && + !(intersect.object.type === "GridHelper") + ); + if (intersects.length > 0) { + const { x, y, z } = intersects[0].point; + + // ✅ Explicitly define position as a tuple + const newWidget: { id: string; type: string; position: [number, number, number] } = { + id: generateUniqueId(), + type: widgetSelect, + position: [x, y, z], // Ensures TypeScript recognizes it as a tuple + }; + + + let response = await adding3dWidgets(selectedZone.zoneId, organization, newWidget) + + + // ✅ Store widgets uniquely for each zone + setZoneWidgetData((prev) => ({ + ...prev, + [selectedZone.zoneId]: [...(prev[selectedZone.zoneId] || []), newWidget], + })); } }; + canvasElement.addEventListener("drop", onDrop); return () => { - canvasElement.removeEventListener("drop", onDrop) - // setWidgetSelect() + canvasElement.removeEventListener("drop", onDrop); }; - }, [widgetSelect, activeModule, widgetSubOption]); + }, [widgetSelect, activeModule, selectedZone.zoneId, widgetSubOption]); + + // Get widgets for the currently active zone + const activeZoneWidgets = zoneWidgetData[selectedZone.zoneId] || []; return ( <> - {zoneWidgets[selectedZone.zoneId]?.["ui-Widget 1"]?.map((pos, index) => ( - - ))} - {zoneWidgets[selectedZone.zoneId]?.["ui-Widget 2"]?.map((pos, index) => ( - - ))} - {zoneWidgets[selectedZone.zoneId]?.["ui-Widget 3"]?.map((pos, index) => ( - - ))} - {zoneWidgets[selectedZone.zoneId]?.["ui-Widget 4"]?.map((pos, index) => ( - - ))} + {activeZoneWidgets.map(({ id, type, position }) => { + switch (type) { + case "ui-Widget 1": + return ; + case "ui-Widget 2": + return ; + case "ui-Widget 3": + return ; + case "ui-Widget 4": + return ; + default: + return null; + } + })} ); } + diff --git a/app/src/components/ui/componets/DroppedFloatingWidgets.tsx b/app/src/components/ui/componets/DroppedFloatingWidgets.tsx index 272051c..5526902 100644 --- a/app/src/components/ui/componets/DroppedFloatingWidgets.tsx +++ b/app/src/components/ui/componets/DroppedFloatingWidgets.tsx @@ -1,3 +1,4 @@ + import { WalletIcon } from "../../icons/3dChartIcons"; import { useEffect, useRef, useState } from "react"; import { @@ -14,6 +15,7 @@ import { DeleteIcon, } from "../../icons/ExportCommonIcons"; import DistanceLines from "./DistanceLines"; // Import the DistanceLines component +import { deleteFloatingWidgetApi } from "../../../services/realTimeVisulization/zoneData/deleteFloatingWidget"; interface DraggingState { zone: string; @@ -26,12 +28,25 @@ interface DraggingState { }; } +interface DraggingState { + zone: string; + index: number; + initialPosition: { + top: number | "auto"; + left: number | "auto"; + right: number | "auto"; + bottom: number | "auto"; + }; +} const DroppedObjects: React.FC = () => { const zones = useDroppedObjectsStore((state) => state.zones); const [openKebabId, setOpenKebabId] = useState(null); const updateObjectPosition = useDroppedObjectsStore( (state) => state.updateObjectPosition ); + const deleteObject = useDroppedObjectsStore((state) => state.deleteObject); + + const duplicateObject = useDroppedObjectsStore((state) => state.duplicateObject); const [draggingIndex, setDraggingIndex] = useState( null ); @@ -62,6 +77,28 @@ const DroppedObjects: React.FC = () => { if (zoneEntries.length === 0) return null; const [zoneName, zone] = zoneEntries[0]; + function handleDuplicate(zoneName: string, index: number) { + setOpenKebabId(null) + duplicateObject(zoneName, index); // Call the duplicateObject method from the store + } + + async function handleDelete(zoneName: string, id: string, index: number) { + try { + const email = localStorage.getItem("email") || ""; + const organization = email?.split("@")[1]?.split(".")[0]; + + let res = await deleteFloatingWidgetApi(id, organization); + console.log('res: ', res); + + if (res.message === "FloatingWidget deleted successfully") { + deleteObject(zoneName, index); // Call the deleteObject method from the store + } + } catch (error) { + console.error("Error deleting floating widget:", error); + } + } + + const handlePointerDown = (event: React.PointerEvent, index: number) => { const obj = zone.objects[index]; const container = document.getElementById("real-time-vis-canvas"); @@ -228,11 +265,12 @@ const DroppedObjects: React.FC = () => { animationRef.current = null; } } catch (error) { - console.error("Error in handlePointerUp:", error); + } }; const handleKebabClick = (id: string, event: React.MouseEvent) => { + event.stopPropagation(); setOpenKebabId((prevId) => (prevId === id ? null : id)); }; @@ -337,13 +375,19 @@ const DroppedObjects: React.FC = () => {
{openKebabId === obj.id && (
-
+
{ + event.stopPropagation(); + handleDuplicate(zoneName, index); // Call the duplicate handler + }}>
Duplicate
-
+
{ + event.stopPropagation(); + handleDelete(zoneName, obj.id, index); // Call the delete handler + }}>
@@ -388,4 +432,4 @@ const DroppedObjects: React.FC = () => { export default DroppedObjects; -// in pointer move even when i goes to top right the value not changes to top right same problem in bottom left + diff --git a/app/src/components/ui/componets/RealTimeVisulization.tsx b/app/src/components/ui/componets/RealTimeVisulization.tsx index 45db987..3f81518 100644 --- a/app/src/components/ui/componets/RealTimeVisulization.tsx +++ b/app/src/components/ui/componets/RealTimeVisulization.tsx @@ -63,6 +63,7 @@ const RealTimeVisulization: React.FC = () => { const organization = email?.split("@")[1]?.split(".")[0]; try { const response = await getZone2dData(organization); + if (!Array.isArray(response)) { return; } @@ -83,7 +84,7 @@ const RealTimeVisulization: React.FC = () => { ); setZonesData(formattedData); } catch (error) { - console.log("error: ", error); + } } @@ -109,7 +110,7 @@ const RealTimeVisulization: React.FC = () => { }); }, [selectedZone]); - useEffect(() => {}, [floatingWidgets]); + // useEffect(() => {}, [floatingWidgets]); const handleDrop = async (event: React.DragEvent) => { try { @@ -135,6 +136,7 @@ const RealTimeVisulization: React.FC = () => { id: generateUniqueId(), position: determinePosition(canvasRect, relativeX, relativeY), }; + console.log('newObject: ', newObject); let response = await addingFloatingWidgets( selectedZone.zoneId, @@ -150,7 +152,6 @@ const RealTimeVisulization: React.FC = () => { .getState() .setZone(selectedZone.zoneName, selectedZone.zoneId); } - // Add the dropped object to the zone if the API call is successful if (response.message === "FloatWidget created successfully") { useDroppedObjectsStore @@ -171,7 +172,7 @@ const RealTimeVisulization: React.FC = () => { ], }, })); - } catch (error) {} + } catch (error) { } }; return ( @@ -198,7 +199,7 @@ const RealTimeVisulization: React.FC = () => { >
- + {activeModule === "visualization" && selectedZone.zoneName !== "" && } {activeModule === "visualization" && ( <> { +// const { getTemplate } = useTemplateStore.getState(); +// const { setSelectedZone } = useSelectedZoneStore.getState(); + +// // Find the template by ID +// const template: Template | undefined = getTemplate(templateId); + +// if (!template) { +// console.error("Template not found!"); +// return; +// } + +// // Update the selected zone with the template data +// setSelectedZone((prev) => ({ +// ...prev, +// panelOrder: template.panelOrder, +// activeSides: Array.from(new Set([...prev.activeSides, ...template.panelOrder])), +// widgets: template.widgets, // Keep widget structure the same +// })); + +// console.log("Dropped template applied:", template); +// }; diff --git a/app/src/modules/simulation/simulation.tsx b/app/src/modules/simulation/simulation.tsx index 10934fb..0682f62 100644 --- a/app/src/modules/simulation/simulation.tsx +++ b/app/src/modules/simulation/simulation.tsx @@ -14,7 +14,6 @@ function Simulation() { const [processes, setProcesses] = useState([]); useEffect(() => { - console.log('simulationPaths: ', simulationPaths); }, [simulationPaths]); // useEffect(() => { diff --git a/app/src/modules/visualization/handleSaveTemplate.ts b/app/src/modules/visualization/handleSaveTemplate.ts index c489688..a71e654 100644 --- a/app/src/modules/visualization/handleSaveTemplate.ts +++ b/app/src/modules/visualization/handleSaveTemplate.ts @@ -1,74 +1,89 @@ +import { saveTemplateApi } from "../../services/realTimeVisulization/zoneData/saveTempleteApi"; import { Template } from "../../store/useTemplateStore"; import { captureVisualization } from "./captureVisualization"; type HandleSaveTemplateProps = { addTemplate: (template: Template) => void; + floatingWidget: []; // Updated type from `[]` to `any[]` for clarity + widgets3D: []; // Updated type from `[]` to `any[]` for clarity selectedZone: { - panelOrder: string[]; // Adjust the type based on actual data structure - widgets: any[]; // Replace `any` with the actual widget type + panelOrder: string[]; + widgets: any[]; }; templates?: Template[]; }; -// Generate a unique ID (placeholder function) +// Generate a unique ID const generateUniqueId = (): string => { return `${Date.now()}-${Math.random().toString(36).substring(2, 15)}`; }; -// Refactored function export const handleSaveTemplate = async ({ addTemplate, + floatingWidget, + widgets3D, selectedZone, templates = [], }: HandleSaveTemplateProps): Promise => { try { // Check if the selected zone has any widgets if (!selectedZone.widgets || selectedZone.widgets.length === 0) { - console.warn("Cannot save an empty template."); + console.warn("No widgets found in the selected zone."); return; } // Check if the template already exists - const isDuplicate = templates.some((template) => { - const isSamePanelOrder = + const isDuplicate = templates.some( + (template) => JSON.stringify(template.panelOrder) === - JSON.stringify(selectedZone.panelOrder); - const isSameWidgets = + JSON.stringify(selectedZone.panelOrder) && JSON.stringify(template.widgets) === - JSON.stringify(selectedZone.widgets); - return isSamePanelOrder && isSameWidgets; - }); + JSON.stringify(selectedZone.widgets) + ); if (isDuplicate) { - console.warn("This template already exists."); return; } // Capture visualization snapshot const snapshot = await captureVisualization(); - if (!snapshot) { - console.error("Failed to capture visualization snapshot."); - return; - } - + console.log("snapshot: ", snapshot); + // if (!snapshot) { + // return; + // } // Create a new template const newTemplate: Template = { id: generateUniqueId(), - name: `Template ${Date.now()}`, + name: `Template ${new Date().toISOString()}`, // Better name formatting panelOrder: selectedZone.panelOrder, widgets: selectedZone.widgets, snapshot, + floatingWidget, + widgets3D, }; - console.log("Saving template:", newTemplate); + // Extract organization from email + const email = localStorage.getItem("email") || ""; + const organization = email.includes("@") + ? email.split("@")[1]?.split(".")[0] + : ""; + + if (!organization) { + console.error("Organization could not be determined from email."); + return; + } // Save the template try { + const response = await saveTemplateApi(organization, newTemplate); + console.log("Save API Response:", response); + + // Add template only if API call succeeds addTemplate(newTemplate); - } catch (error) { - console.error("Failed to add template:", error); + } catch (apiError) { + console.error("Error saving template to API:", apiError); } } catch (error) { - console.error("Failed to save template:", error); + console.error("Error in handleSaveTemplate:", error); } }; diff --git a/app/src/services/realTimeVisulization/zoneData/add3dWidget.ts b/app/src/services/realTimeVisulization/zoneData/add3dWidget.ts new file mode 100644 index 0000000..82562b7 --- /dev/null +++ b/app/src/services/realTimeVisulization/zoneData/add3dWidget.ts @@ -0,0 +1,36 @@ +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 adding3dWidgets = async ( + zoneId: string, + 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`, + { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ organization, zoneId, widget }), + } + ); + + 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/services/realTimeVisulization/zoneData/addFloatingWidgets.ts b/app/src/services/realTimeVisulization/zoneData/addFloatingWidgets.ts index 338f26b..d73ec0d 100644 --- a/app/src/services/realTimeVisulization/zoneData/addFloatingWidgets.ts +++ b/app/src/services/realTimeVisulization/zoneData/addFloatingWidgets.ts @@ -1,5 +1,6 @@ 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 addingFloatingWidgets = async ( zoneId: string, organization: string, diff --git a/app/src/services/realTimeVisulization/zoneData/deleteFloatingWidget.ts b/app/src/services/realTimeVisulization/zoneData/deleteFloatingWidget.ts new file mode 100644 index 0000000..85c96b8 --- /dev/null +++ b/app/src/services/realTimeVisulization/zoneData/deleteFloatingWidget.ts @@ -0,0 +1,35 @@ +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 deleteFloatingWidgetApi = async ( + floatWidgetID: string, + organization: string +) => { + console.log('organization: ', organization); + console.log('floatWidgetID: ', floatWidgetID); + try { + const response = await fetch( + `${url_Backend_dwinzo}/api/v2/floatwidget/delete`, + { + method: "PATCH", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ organization, floatWidgetID }), + } + ); + + 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/deleteTemplate.ts b/app/src/services/realTimeVisulization/zoneData/deleteTemplate.ts new file mode 100644 index 0000000..e452f6d --- /dev/null +++ b/app/src/services/realTimeVisulization/zoneData/deleteTemplate.ts @@ -0,0 +1,32 @@ +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 deleteTemplateApi = async ( + templateID: string, + organization?: string +) => { + try { + const response = await fetch( + `${url_Backend_dwinzo}/api/v2/TemplateDelete/${templateID}/${organization}`, + { + method: "PATCH", + headers: { + "Content-Type": "application/json", + }, + } + ); + + if (!response.ok) { + throw new Error("Failed to delete template "); + } + + 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/get3dWidgetData.ts b/app/src/services/realTimeVisulization/zoneData/get3dWidgetData.ts new file mode 100644 index 0000000..b5b6200 --- /dev/null +++ b/app/src/services/realTimeVisulization/zoneData/get3dWidgetData.ts @@ -0,0 +1,25 @@ +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 get3dWidgetZoneData = async ( + ZoneId?: string, + organization?: string +) => { + try { + const response = await fetch( + `${url_Backend_dwinzo}/api/v2/3dwidgetData/${ZoneId}/${organization}`, + { + method: "GET", + headers: { + "Content-Type": "application/json", + }, + } + ); + if (!response.ok) { + throw new Error("Failed to fetch Zone3dWidgetData"); + } + + return await response.json(); + } catch (error: any) { + throw new Error(error.message); + } +}; diff --git a/app/src/services/realTimeVisulization/zoneData/getSelect2dZoneData.ts b/app/src/services/realTimeVisulization/zoneData/getSelect2dZoneData.ts index 26a88c5..00d4dfe 100644 --- a/app/src/services/realTimeVisulization/zoneData/getSelect2dZoneData.ts +++ b/app/src/services/realTimeVisulization/zoneData/getSelect2dZoneData.ts @@ -1,4 +1,5 @@ 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 getSelect2dZoneData = async ( ZoneId?: string, diff --git a/app/src/services/realTimeVisulization/zoneData/getTemplate.ts b/app/src/services/realTimeVisulization/zoneData/getTemplate.ts new file mode 100644 index 0000000..a3aa3a3 --- /dev/null +++ b/app/src/services/realTimeVisulization/zoneData/getTemplate.ts @@ -0,0 +1,23 @@ +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 getTemplateData = async (organization?: string) => { + try { + const response = await fetch( + `${url_Backend_dwinzo}/api/v2/templateData/${organization}`, + { + method: "GET", + headers: { + "Content-Type": "application/json", + }, + } + ); + + if (!response.ok) { + throw new Error("Failed to fetch ZoneFloatingData"); + } + + return await response.json(); + } catch (error: any) { + throw new Error(error.message); + } +}; diff --git a/app/src/services/realTimeVisulization/zoneData/getZones.ts b/app/src/services/realTimeVisulization/zoneData/getZones.ts index a760959..8dbf79a 100644 --- a/app/src/services/realTimeVisulization/zoneData/getZones.ts +++ b/app/src/services/realTimeVisulization/zoneData/getZones.ts @@ -1,4 +1,5 @@ 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 getZoneData = async (zoneId: string, organization: string) => { console.log("organization: ", organization); @@ -14,7 +15,6 @@ export const getZoneData = async (zoneId: string, organization: string) => { } ); - if (!response.ok) { throw new Error("Failed to fetch zoneData"); } diff --git a/app/src/services/realTimeVisulization/zoneData/loadTemplate.ts b/app/src/services/realTimeVisulization/zoneData/loadTemplate.ts new file mode 100644 index 0000000..915160d --- /dev/null +++ b/app/src/services/realTimeVisulization/zoneData/loadTemplate.ts @@ -0,0 +1,33 @@ +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 loadTempleteApi = async ( + templateID: string, + zoneId: string, + organization: string +) => { + try { + const response = await fetch( + `${url_Backend_dwinzo}/api/v2/TemplatetoZone`, + { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ organization, zoneId, templateID }), + } + ); + + 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/services/realTimeVisulization/zoneData/saveTempleteApi.ts b/app/src/services/realTimeVisulization/zoneData/saveTempleteApi.ts new file mode 100644 index 0000000..5c18031 --- /dev/null +++ b/app/src/services/realTimeVisulization/zoneData/saveTempleteApi.ts @@ -0,0 +1,28 @@ +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); + try { + const response = await fetch(`${url_Backend_dwinzo}/api/v2/template/save`, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ organization, template }), + }); + + if (!response.ok) { + throw new Error("Failed to save template zone"); + } + + const result = await response.json(); + console.log('result: ', result); + 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/store.ts b/app/src/store/store.ts index a64f417..f24da0c 100644 --- a/app/src/store/store.ts +++ b/app/src/store/store.ts @@ -375,3 +375,4 @@ export const useWidgetSubOption = create((set: any) => ({ widgetSubOption: "2D", setWidgetSubOption: (x: any) => set({ widgetSubOption: x }), })); + diff --git a/app/src/store/useDroppedObjectsStore.ts b/app/src/store/useDroppedObjectsStore.ts index 5ded701..83f3a8a 100644 --- a/app/src/store/useDroppedObjectsStore.ts +++ b/app/src/store/useDroppedObjectsStore.ts @@ -1,4 +1,5 @@ import { create } from "zustand"; +import { addingFloatingWidgets } from "../services/realTimeVisulization/zoneData/addFloatingWidgets"; type DroppedObject = { className: string; @@ -35,6 +36,8 @@ type DroppedObjectsState = { bottom: number | "auto"; } ) => void; + deleteObject: (zoneName: string, index: number) => void; // Add this line + duplicateObject: (zoneName: string, index: number) => void; // Add this line }; export const useDroppedObjectsStore = create((set) => ({ @@ -73,6 +76,72 @@ export const useDroppedObjectsStore = create((set) => ({ }, }; }), + + deleteObject: (zoneName: string, index: number) => + set((state) => { + const zone = state.zones[zoneName]; + if (!zone) return state; + return { + zones: { + [zoneName]: { + ...zone, + objects: zone.objects.filter((_, i) => i !== index), // Remove object at the given index + }, + }, + }; + }), + duplicateObject: async (zoneName: string, index: number) => { + const state = useDroppedObjectsStore.getState(); // Get the current state + const zone = state.zones[zoneName]; + + if (!zone) return; + + const originalObject = zone.objects[index]; + if (!originalObject) return; + + const email = localStorage.getItem("email") || ""; + const organization = email?.split("@")[1]?.split(".")[0]; + + // Create a shallow copy of the object with a unique ID and slightly adjusted position + const duplicatedObject: DroppedObject = { + ...originalObject, + id: `${originalObject.id}-copy-${Date.now()}`, // Unique ID + position: { + ...originalObject.position, + top: + typeof originalObject.position.top === "number" + ? originalObject.position.top + 20 // Offset vertically + : originalObject.position.top, + left: + typeof originalObject.position.left === "number" + ? originalObject.position.left + 20 // Offset horizontally + : originalObject.position.left, + }, + }; + + console.log("zone: ", zone.zoneId); + console.log("duplicatedObject: ", duplicatedObject); + + // Make async API call outside of Zustand set function + // let response = await addingFloatingWidgets( + // zone.zoneId, + // organization, + // duplicatedObject + // ); + + // if (response.message === "FloatWidget created successfully") { + // Update the state inside `set` + useDroppedObjectsStore.setState((state) => ({ + zones: { + ...state.zones, + [zoneName]: { + ...state.zones[zoneName], + objects: [...state.zones[zoneName].objects, duplicatedObject], // Append duplicated object + }, + }, + })); + // } + }, })); export interface DroppedObjects { @@ -91,4 +160,12 @@ export interface Zones { objects: DroppedObject[]; } +export const use3DWidget = create((set: any) => ({ + widgets3D: [], + setWidgets3D: (x: any) => set({ widgets3D: x }), +})); +export const useFloatingWidget = create((set: any) => ({ + floatingWidget: [], + setFloatingWidget: (x: any) => set({ floatingWidget: x }), +})); diff --git a/app/src/store/useTemplateStore.ts b/app/src/store/useTemplateStore.ts index 2adcd2f..d416154 100644 --- a/app/src/store/useTemplateStore.ts +++ b/app/src/store/useTemplateStore.ts @@ -1,7 +1,5 @@ import { create } from "zustand"; -// type Side = "top" | "bottom" | "left" | "right"; - export interface Widget { id: string; type: string; @@ -15,21 +13,34 @@ export interface Template { name: string; panelOrder: string[]; widgets: Widget[]; - snapshot?: string | null; // Add an optional image property (base64) + floatingWidget: any[]; // Fixed empty array type + widgets3D: any[]; // Fixed empty array type + snapshot?: string | null; } interface TemplateStore { templates: Template[]; addTemplate: (template: Template) => void; + setTemplates: (templates: Template[]) => void; // Changed from `setTemplate` removeTemplate: (id: string) => void; } export const useTemplateStore = create((set) => ({ templates: [], + + // Add a new template to the list addTemplate: (template) => set((state) => ({ templates: [...state.templates, template], })), + + // Set (replace) the templates list with a new array + setTemplates: (templates) => + set(() => ({ + templates, // Ensures no duplication + })), + + // Remove a template by ID removeTemplate: (id) => set((state) => ({ templates: state.templates.filter((t) => t.id !== id), @@ -37,3 +48,4 @@ export const useTemplateStore = create((set) => ({ })); export default useTemplateStore; + diff --git a/app/src/styles/pages/realTimeViz.scss b/app/src/styles/pages/realTimeViz.scss index b080840..5df1bf7 100644 --- a/app/src/styles/pages/realTimeViz.scss +++ b/app/src/styles/pages/realTimeViz.scss @@ -543,8 +543,6 @@ color: white; } - - .floating-wrapper { .icon { width: 25px !important; @@ -612,9 +610,6 @@ .dublicate { cursor: not-allowed; } - - - } } @@ -622,12 +617,6 @@ - - - - - -/* General styles for all distance lines */ .distance-line { position: absolute; border-style: dashed; From d4e4952de4608012f441ab93727d7d47de128e57 Mon Sep 17 00:00:00 2001 From: Gomathi9520 Date: Mon, 31 Mar 2025 19:25:44 +0530 Subject: [PATCH 08/11] conflicts resolved --- app/src/components/ui/componets/DroppedFloatingWidgets.tsx | 3 +-- app/src/pages/Project.tsx | 2 +- .../realTimeVisulization/zoneData/addFloatingWidgets.ts | 3 +++ app/src/services/realTimeVisulization/zoneData/addWidgets.ts | 4 ++-- .../services/realTimeVisulization/zoneData/getFloatingData.ts | 4 ++-- app/src/services/realTimeVisulization/zoneData/getZoneData.ts | 4 ++-- app/src/services/realTimeVisulization/zoneData/panel.ts | 4 ++-- 7 files changed, 13 insertions(+), 11 deletions(-) diff --git a/app/src/components/ui/componets/DroppedFloatingWidgets.tsx b/app/src/components/ui/componets/DroppedFloatingWidgets.tsx index 5526902..e099452 100644 --- a/app/src/components/ui/componets/DroppedFloatingWidgets.tsx +++ b/app/src/components/ui/componets/DroppedFloatingWidgets.tsx @@ -265,12 +265,11 @@ const DroppedObjects: React.FC = () => { animationRef.current = null; } } catch (error) { - + console.error("Error in handlePointerUp:", error); } }; const handleKebabClick = (id: string, event: React.MouseEvent) => { - event.stopPropagation(); setOpenKebabId((prevId) => (prevId === id ? null : id)); }; diff --git a/app/src/pages/Project.tsx b/app/src/pages/Project.tsx index a7800e1..8e426aa 100644 --- a/app/src/pages/Project.tsx +++ b/app/src/pages/Project.tsx @@ -52,7 +52,7 @@ const Project: React.FC = () => { return (
- {loadingProgress && } + {/* {loadingProgress && } */} {!isPlaying && ( <> diff --git a/app/src/services/realTimeVisulization/zoneData/addFloatingWidgets.ts b/app/src/services/realTimeVisulization/zoneData/addFloatingWidgets.ts index d73ec0d..fb644c6 100644 --- a/app/src/services/realTimeVisulization/zoneData/addFloatingWidgets.ts +++ b/app/src/services/realTimeVisulization/zoneData/addFloatingWidgets.ts @@ -6,6 +6,9 @@ export const addingFloatingWidgets = async ( organization: string, widget: {} ) => { + console.log('organization: ', organization); + console.log('widget: ', widget); + console.log('zoneId: ', zoneId); try { const response = await fetch( `${url_Backend_dwinzo}/api/v2/floatwidget/save`, diff --git a/app/src/services/realTimeVisulization/zoneData/addWidgets.ts b/app/src/services/realTimeVisulization/zoneData/addWidgets.ts index 8539e54..08969ab 100644 --- a/app/src/services/realTimeVisulization/zoneData/addWidgets.ts +++ b/app/src/services/realTimeVisulization/zoneData/addWidgets.ts @@ -1,5 +1,5 @@ -let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`; -// let url_Backend_dwinzo = `http://192.168.0.102:5000`; +// 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 addingWidgets = async ( zoneId: string, organization: string, diff --git a/app/src/services/realTimeVisulization/zoneData/getFloatingData.ts b/app/src/services/realTimeVisulization/zoneData/getFloatingData.ts index 80d2b19..0246175 100644 --- a/app/src/services/realTimeVisulization/zoneData/getFloatingData.ts +++ b/app/src/services/realTimeVisulization/zoneData/getFloatingData.ts @@ -1,5 +1,5 @@ -let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`; -// let url_Backend_dwinzo = `http://192.168.0.102:5000`; +// 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 getFloatingZoneData = async ( ZoneId?: string, organization?: string diff --git a/app/src/services/realTimeVisulization/zoneData/getZoneData.ts b/app/src/services/realTimeVisulization/zoneData/getZoneData.ts index efbac3b..f68ef15 100644 --- a/app/src/services/realTimeVisulization/zoneData/getZoneData.ts +++ b/app/src/services/realTimeVisulization/zoneData/getZoneData.ts @@ -1,5 +1,5 @@ -let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`; -// let url_Backend_dwinzo = `http://192.168.0.102:5000`; +// 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 getZone2dData = async (organization?: string) => { try { const response = await fetch( diff --git a/app/src/services/realTimeVisulization/zoneData/panel.ts b/app/src/services/realTimeVisulization/zoneData/panel.ts index 82f1289..6d93d1f 100644 --- a/app/src/services/realTimeVisulization/zoneData/panel.ts +++ b/app/src/services/realTimeVisulization/zoneData/panel.ts @@ -1,5 +1,5 @@ -let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`; -// let url_Backend_dwinzo = `http://192.168.0.102:5000`; +// let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`; +let url_Backend_dwinzo = `http://192.168.0.102:5000`; type Side = "top" | "bottom" | "left" | "right"; export const panelData = async ( From e3a85c81e5c4cc4befa9f97349c6dddb0f874d87 Mon Sep 17 00:00:00 2001 From: Jerald-Golden-B Date: Mon, 31 Mar 2025 19:38:23 +0530 Subject: [PATCH 09/11] Refactor socket initialization to include organization and enhance asset event handling; remove unused console logs and improve asset data structure --- .../components/layout/sidebarLeft/Assets.tsx | 12 +- .../geomentries/assets/addAssetModel.ts | 106 ++++++++++++------ .../builder/groups/floorItemsGroup.tsx | 4 + .../collaboration/socketResponses.dev.tsx | 3 +- app/src/modules/scene/world/world.tsx | 2 +- .../simulation/behaviour/behaviour.tsx | 6 - app/src/pages/Project.tsx | 2 +- .../assest/floorAsset/setFloorItemApi.ts | 24 +++- .../services/simulation/getAssetEventType.ts | 1 - app/src/store/store.ts | 13 +-- app/src/types/world/worldConstants.ts | 26 ----- app/src/types/world/worldTypes.d.ts | 23 +++- 12 files changed, 137 insertions(+), 85 deletions(-) diff --git a/app/src/components/layout/sidebarLeft/Assets.tsx b/app/src/components/layout/sidebarLeft/Assets.tsx index f8733dc..2498e63 100644 --- a/app/src/components/layout/sidebarLeft/Assets.tsx +++ b/app/src/components/layout/sidebarLeft/Assets.tsx @@ -9,6 +9,7 @@ import { getCategoryAsset } from "../../../services/factoryBuilder/assest/assets import arch from "../../../assets/gltf-glb/arch.glb"; import door from "../../../assets/gltf-glb/door.glb"; import window from "../../../assets/gltf-glb/window.glb"; +import { useSelectedItem } from "../../../store/store"; interface AssetProp { filename: string; thumbnail?: string; @@ -23,6 +24,7 @@ interface AssetProp { CreatedBy?: String; } const Assets: React.FC = () => { + const { setSelectedItem } = useSelectedItem(); const [searchValue, setSearchValue] = useState(""); const [selectedCategory, setSelectedCategory] = useState(null); const [filteredAsset, setFilteredAsset] = useState([]); @@ -85,11 +87,11 @@ const Assets: React.FC = () => { try { const res = await getCategoryAsset(asset); setFilteredAsset(res || []); // Ensure it's always an array - } catch (error) {} + } catch (error) { } } }; - useEffect(() => {}, [filteredAsset]); + useEffect(() => { }, [filteredAsset]); return (
@@ -102,7 +104,10 @@ const Assets: React.FC = () => { {/* Back Button */}
setSelectedCategory(null)} + onClick={() => { + setSelectedCategory(null); + setFilteredAsset([]); + }} > ← Back
@@ -116,6 +121,7 @@ const Assets: React.FC = () => { src={asset?.thumbnail} alt={asset.filename} className="asset-image" + onPointerDown={() => setSelectedItem({ name: asset.filename, id: asset.modelfileID })} /> )}
diff --git a/app/src/modules/builder/geomentries/assets/addAssetModel.ts b/app/src/modules/builder/geomentries/assets/addAssetModel.ts index e0e50c6..65832f8 100644 --- a/app/src/modules/builder/geomentries/assets/addAssetModel.ts +++ b/app/src/modules/builder/geomentries/assets/addAssetModel.ts @@ -9,6 +9,8 @@ import { retrieveGLTF, storeGLTF } from '../../../../utils/indexDB/idbUtils'; // import { setFloorItemApi } from '../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi'; import { Socket } from 'socket.io-client'; import * as CONSTANTS from '../../../../types/world/worldConstants'; +import { getAssetEventType } from '../../../../services/simulation/getAssetEventType'; +import { setFloorItemApi } from '../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi'; async function addAssetModel( raycaster: THREE.Raycaster, @@ -58,6 +60,7 @@ async function addAssetModel( if (intersectPoint.y < 0) { intersectPoint = new THREE.Vector3(intersectPoint.x, 0, intersectPoint.z); } + console.log('selectedItem: ', selectedItem); const cachedModel = THREE.Cache.get(selectedItem.id); if (cachedModel) { // console.log(`[Cache] Fetching ${selectedItem.name}`); @@ -136,51 +139,88 @@ async function handleModelLoad( modelname: selectedItem.name, modelfileID: selectedItem.id, position: [intersectPoint!.x, intersectPoint!.y, intersectPoint!.z], - rotation: { x: model.rotation.x, y: model.rotation.y, z: model.rotation.z, }, + rotation: { x: model.rotation.x, y: model.rotation.y, z: model.rotation.z }, isLocked: false, isVisible: true }; - setFloorItems((prevItems) => { - const updatedItems = [...(prevItems || []), newFloorItem]; - localStorage.setItem("FloorItems", JSON.stringify(updatedItems)); - return updatedItems; - }); - const email = localStorage.getItem("email"); const organization = email ? email.split("@")[1].split(".")[0] : "default"; - //REST + getAssetEventType(selectedItem.id, organization).then(async (res) => { + console.log('res: ', res); - // await setFloorItemApi( - // organization, - // newFloorItem.modeluuid, - // newFloorItem.modelname, - // newFloorItem.position, - // { "x": model.rotation.x, "y": model.rotation.y, "z": model.rotation.z }, - // newFloorItem.modelfileID!, - // false, - // true, - // ); + if (res.type === "Conveyor") { + const pointUUIDs = res.points.map(() => THREE.MathUtils.generateUUID()); - //SOCKET + const eventData: Extract = { + type: 'Conveyor', + points: res.points.map((point: any, index: number) => ({ + uuid: pointUUIDs[index], + position: point.position as [number, number, number], + rotation: point.rotation as [number, number, number], + actions: [{ + uuid: THREE.MathUtils.generateUUID(), + name: 'Action 1', + type: 'Inherit', + material: 'Inherit', + delay: 'Inherit', + spawnInterval: 'Inherit', + isUsed: false + }], + triggers: [], + connections: { + source: { pathUUID: model.uuid, pointUUID: pointUUIDs[index] }, + targets: [] + } + })), + speed: 'Inherit' + }; - const data = { - organization, - modeluuid: newFloorItem.modeluuid, - modelname: newFloorItem.modelname, - modelfileID: newFloorItem.modelfileID, - position: newFloorItem.position, - rotation: { x: model.rotation.x, y: model.rotation.y, z: model.rotation.z }, - isLocked: false, - isVisible: true, - socketId: socket.id, - }; + console.log('eventData: ', eventData); + newFloorItem.eventData = eventData; + } - socket.emit("v1:FloorItems:set", data); + setFloorItems((prevItems) => { + const updatedItems = [...(prevItems || []), newFloorItem]; + localStorage.setItem("FloorItems", JSON.stringify(updatedItems)); + return updatedItems; + }); - gsap.to(model.position, { y: newFloorItem.position[1], duration: 1.5, ease: "power2.out" }); - gsap.to(model.scale, { x: 1, y: 1, z: 1, duration: 1.5, ease: "power2.out", onComplete: () => { toast.success("Model Added!"); } }); + // API + + // await setFloorItemApi( + // organization, + // newFloorItem.modeluuid, + // newFloorItem.modelname, + // newFloorItem.modelfileID, + // newFloorItem.position, + // { x: model.rotation.x, y: model.rotation.y, z: model.rotation.z }, + // false, + // true, + // newFloorItem.eventData + // ); + + // SOCKET + + const data = { + organization, + modeluuid: newFloorItem.modeluuid, + modelname: newFloorItem.modelname, + modelfileID: newFloorItem.modelfileID, + position: newFloorItem.position, + rotation: { x: model.rotation.x, y: model.rotation.y, z: model.rotation.z }, + isLocked: false, + isVisible: true, + eventData: newFloorItem.eventData, + socketId: socket.id + }; + + socket.emit("v2:model-asset:add", data); + + gsap.to(model.position, { y: newFloorItem.position[1], duration: 1.5, ease: "power2.out" }); + gsap.to(model.scale, { x: 1, y: 1, z: 1, duration: 1.5, ease: "power2.out", onComplete: () => { toast.success("Model Added!"); } }); + }); } export default addAssetModel; diff --git a/app/src/modules/builder/groups/floorItemsGroup.tsx b/app/src/modules/builder/groups/floorItemsGroup.tsx index cf9d6b8..3b38d01 100644 --- a/app/src/modules/builder/groups/floorItemsGroup.tsx +++ b/app/src/modules/builder/groups/floorItemsGroup.tsx @@ -60,6 +60,7 @@ const FloorItemsGroup = ({ itemsGroup, hoveredDeletableFloorItem, AttachedObject }; getFloorItems(organization).then((data) => { + console.log('data: ', data); const uniqueItems = (data as Types.FloorItems).filter((item, index, self) => index === self.findIndex((t) => t.modelfileID === item.modelfileID) ); @@ -305,7 +306,10 @@ const FloorItemsGroup = ({ itemsGroup, hoveredDeletableFloorItem, AttachedObject }; }, [deleteModels, transformMode, controls, selectedItem, state.camera, state.pointer, activeTool, activeModule]); + useEffect(() => { + console.log('floorItems: ', floorItems); + }, [floorItems]) useFrame(() => { if (controls) diff --git a/app/src/modules/collaboration/socketResponses.dev.tsx b/app/src/modules/collaboration/socketResponses.dev.tsx index 031629f..1a5ef83 100644 --- a/app/src/modules/collaboration/socketResponses.dev.tsx +++ b/app/src/modules/collaboration/socketResponses.dev.tsx @@ -81,7 +81,8 @@ export default function SocketResponses({ // console.log('data: ', data); }) - socket.on('FloorItemsUpdateResponse', async (data: any) => { + socket.on('model-asset:response:updates', async (data: any) => { + console.log('data: ', data); if (socket.id === data.socketId) { return } diff --git a/app/src/modules/scene/world/world.tsx b/app/src/modules/scene/world/world.tsx index eb7d555..76cf539 100644 --- a/app/src/modules/scene/world/world.tsx +++ b/app/src/modules/scene/world/world.tsx @@ -358,7 +358,7 @@ export default function World() { anglesnappedPoint={anglesnappedPoint} /> - + {/* */} diff --git a/app/src/modules/simulation/behaviour/behaviour.tsx b/app/src/modules/simulation/behaviour/behaviour.tsx index afb4ae9..5075384 100644 --- a/app/src/modules/simulation/behaviour/behaviour.tsx +++ b/app/src/modules/simulation/behaviour/behaviour.tsx @@ -2,22 +2,16 @@ import { useFloorItems, useSimulationPaths } from '../../../store/store'; import * as THREE from 'three'; import * as Types from '../../../types/world/worldTypes'; import { useEffect } from 'react'; -import { getAssetEventType } from '../../../services/simulation/getAssetEventType'; function Behaviour() { const { setSimulationPaths } = useSimulationPaths(); const { floorItems } = useFloorItems(); useEffect(() => { - const email = localStorage.getItem('email') - const organization = (email!.split("@")[1]).split(".")[0]; const newPaths: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema)[] = []; floorItems.forEach((item: Types.FloorItemType) => { if (item.modelfileID === "672a090f80d91ac979f4d0bd") { - // getAssetEventType(item.modelfileID, organization).then((res) => { - // console.log('res: ', res); - // }); const point1Position = new THREE.Vector3(0, 0.85, 2.2); const middlePointPosition = new THREE.Vector3(0, 0.85, 0); const point2Position = new THREE.Vector3(0, 0.85, -2.2); diff --git a/app/src/pages/Project.tsx b/app/src/pages/Project.tsx index 41f04a7..7aad5cb 100644 --- a/app/src/pages/Project.tsx +++ b/app/src/pages/Project.tsx @@ -37,8 +37,8 @@ const Project: React.FC = () => { setZones([]); const email = localStorage.getItem("email"); if (email) { - useSocketStore.getState().initializeSocket(email); const Organization = email!.split("@")[1].split(".")[0]; + useSocketStore.getState().initializeSocket(email, Organization); const name = localStorage.getItem("userName"); if (Organization && name) { setOrganization(Organization); diff --git a/app/src/services/factoryBuilder/assest/floorAsset/setFloorItemApi.ts b/app/src/services/factoryBuilder/assest/floorAsset/setFloorItemApi.ts index 31d5b11..f6cd496 100644 --- a/app/src/services/factoryBuilder/assest/floorAsset/setFloorItemApi.ts +++ b/app/src/services/factoryBuilder/assest/floorAsset/setFloorItemApi.ts @@ -1,13 +1,28 @@ -let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`; +let url_Backend_dwinzo = `http://${process.env.REACT_APP_TEST}`; -export const setFloorItemApi = async (organization: string, modeluuid: string, modelname: string, position: Object, rotation: Object, modelfileID: string, isLocked: boolean, isVisible: boolean) => { +export const setFloorItemApi = async ( + organization: string, + modeluuid: string, + modelname: string, + modelfileID: string, + position: Object, + rotation: Object, + isLocked: boolean, + isVisible: boolean, + eventData?: any +) => { try { - const response = await fetch(`${url_Backend_dwinzo}/api/v1/setFloorItems`, { + const body: any = { organization, modeluuid, modelname, position, rotation, modelfileID, isLocked, isVisible }; + if (eventData) { + body.eventData = eventData; + } + + const response = await fetch(`${url_Backend_dwinzo}/api/v2/setasset`, { method: "POST", headers: { "Content-Type": "application/json", }, - body: JSON.stringify({ organization, modeluuid, modelname, position, rotation, modelfileID, isLocked, isVisible }), + body: JSON.stringify(body), }); if (!response.ok) { @@ -15,6 +30,7 @@ export const setFloorItemApi = async (organization: string, modeluuid: string, m } const result = await response.json(); + console.log('result: ', result); return result; } catch (error) { if (error instanceof Error) { diff --git a/app/src/services/simulation/getAssetEventType.ts b/app/src/services/simulation/getAssetEventType.ts index e516feb..a681b12 100644 --- a/app/src/services/simulation/getAssetEventType.ts +++ b/app/src/services/simulation/getAssetEventType.ts @@ -14,7 +14,6 @@ export const getAssetEventType = async (modelId: string, organization: string) = } const result = await response.json(); - console.log('result: ', result); return result; } catch (error) { if (error instanceof Error) { diff --git a/app/src/store/store.ts b/app/src/store/store.ts index 9688448..964a1a7 100644 --- a/app/src/store/store.ts +++ b/app/src/store/store.ts @@ -5,19 +5,16 @@ import { io } from "socket.io-client"; export const useSocketStore = create((set: any, get: any) => ({ socket: null, - initializeSocket: (email: any) => { + initializeSocket: (email: string, organization: string) => { const existingSocket = get().socket; if (existingSocket) { return; } - const socket = io( - `http://${process.env.REACT_APP_SERVER_SOCKET_API_BASE_URL}/`, - { - reconnection: false, - auth: { email }, - } - ); + const socket = io(`http://${process.env.REACT_APP_SERVER_SOCKET_API_BASE_URL}/Builder`, { + reconnection: false, + auth: { email, organization }, + }); set({ socket }); }, diff --git a/app/src/types/world/worldConstants.ts b/app/src/types/world/worldConstants.ts index 66743fb..c9f187b 100644 --- a/app/src/types/world/worldConstants.ts +++ b/app/src/types/world/worldConstants.ts @@ -61,7 +61,6 @@ export type GridConfig = { divisions: number; primaryColor: string; secondaryColor: string; - position2D: [x: number, y: number, z: number]; position3D: [x: number, y: number, z: number]; }; @@ -70,7 +69,6 @@ export type PlaneConfig = { position2D: [x: number, y: number, z: number]; position3D: [x: number, y: number, z: number]; rotation: number; - width: number; height: number; color: string; @@ -78,7 +76,6 @@ export type PlaneConfig = { export type ShadowConfig = { shadowOffset: number; - shadowmapSizewidth: number; shadowmapSizeheight: number; shadowcamerafar: number; @@ -89,10 +86,8 @@ export type ShadowConfig = { shadowcameraright: number; shadowbias: number; shadownormalBias: number; - shadowMaterialPosition: [x: number, y: number, z: number]; shadowMaterialRotation: [x: number, y: number, z: number]; - shadowMaterialOpacity: number; }; @@ -116,12 +111,10 @@ export type PointConfig = { defaultOuterColor: string; deleteColor: string; boxScale: [number, number, number]; - wallOuterColor: string; floorOuterColor: string; aisleOuterColor: string; zoneOuterColor: string; - snappingThreshold: number; }; @@ -129,17 +122,13 @@ export type LineConfig = { tubularSegments: number; radius: number; radialSegments: number; - wallName: string; floorName: string; aisleName: string; zoneName: string; referenceName: string; - lineIntersectionPoints: number; - defaultColor: string; - wallColor: string; floorColor: string; aisleColor: string; @@ -156,7 +145,6 @@ export type WallConfig = { export type FloorConfig = { defaultColor: string; height: number; - textureScale: number; }; @@ -168,13 +156,11 @@ export type RoofConfig = { export type AisleConfig = { width: number; height: number; - defaultColor: number; }; export type ZoneConfig = { defaultColor: string; - color: string; }; @@ -199,9 +185,7 @@ export const firstPersonControls: Controls = { minDistance: 0, // Minimum distance from the target maxDistance: 0, // Maximum distance from the target maxPolarAngle: Math.PI, // Maximum polar angle - leftMouse: 1, // Mouse button for rotation (ROTATE) - forwardSpeed: 0.3, // Speed of forward movement backwardSpeed: -0.3, // Speed of backward movement leftSpeed: -0.3, // Speed of left movement @@ -259,7 +243,6 @@ export const gridConfig: GridConfig = { divisions: 75, // Number of divisions in the grid primaryColor: "#d5d5d5", // Primary color of the grid secondaryColor: "#e3e3e3", // Secondary color of the grid - position2D: [0, 0.1, 0], // Position of the grid in 2D view position3D: [0, -0.5, 0], // Position of the grid in 3D view }; @@ -276,7 +259,6 @@ export const planeConfig: PlaneConfig = { export const shadowConfig: ShadowConfig = { shadowOffset: 50, // Offset of the shadow - shadowmapSizewidth: 1024, // Width of the shadow map shadowmapSizeheight: 1024, // Height of the shadow map // shadowmapSizewidth: 8192, // Width of the shadow map @@ -289,10 +271,8 @@ export const shadowConfig: ShadowConfig = { shadowcameraright: 30, // Right plane of the shadow camera shadowbias: -0.001, // Bias of the shadow shadownormalBias: 0.02, // Normal bias of the shadow - shadowMaterialPosition: [0, 0.01, 0], // Position of the shadow material shadowMaterialRotation: [-Math.PI / 2, 0, 0], // Rotation of the shadow material - shadowMaterialOpacity: 0.1, // Opacity of the shadow material }; @@ -316,12 +296,10 @@ export const pointConfig: PointConfig = { defaultOuterColor: "#ffffff", // Default outer color of the points deleteColor: "#ff0000", // Color of the points when deleting boxScale: [0.5, 0.5, 0.5], // Scale of the points - wallOuterColor: "#C7C7C7", // Outer color of the wall points floorOuterColor: "#808080", // Outer color of the floor points aisleOuterColor: "#FBBC05", // Outer color of the aisle points zoneOuterColor: "#007BFF", // Outer color of the zone points - snappingThreshold: 1, // Threshold for snapping }; @@ -329,17 +307,13 @@ export const lineConfig: LineConfig = { tubularSegments: 64, // Number of tubular segments radius: 0.15, // Radius of the lines radialSegments: 8, // Number of radial segments - wallName: "WallLine", // Name of the wall lines floorName: "FloorLine", // Name of the floor lines aisleName: "AisleLine", // Name of the aisle lines zoneName: "ZoneLine", // Name of the zone lines referenceName: "ReferenceLine", // Name of the reference lines - lineIntersectionPoints: 300, // Number of intersection points - defaultColor: "#000000", // Default color of the lines - wallColor: "#C7C7C7", // Color of the wall lines floorColor: "#808080", // Color of the floor lines aisleColor: "#FBBC05", // Color of the aisle lines diff --git a/app/src/types/world/worldTypes.d.ts b/app/src/types/world/worldTypes.d.ts index 3e40f40..4d10a36 100644 --- a/app/src/types/world/worldTypes.d.ts +++ b/app/src/types/world/worldTypes.d.ts @@ -198,9 +198,30 @@ export type FloorItemType = { modelname: string; position: [number, number, number]; rotation: { x: number; y: number; z: number }; - modelfileID?: string; + modelfileID: string; isLocked: boolean; isVisible: boolean; + eventData?: { + type: 'Conveyor'; + points: { + uuid: string; + position: [number, number, number]; + rotation: [number, number, number]; + actions: { uuid: string; name: string; type: string; material: string; delay: number | string; spawnInterval: number | string; isUsed: boolean }[] | []; + triggers: { uuid: string; name: string; type: string; isUsed: boolean; bufferTime: number }[] | []; + connections: { source: { pathUUID: string; pointUUID: string }; targets: { pathUUID: string; pointUUID: string }[] }; + }[]; + speed: number | string; + } | { + type: 'Vehicle'; + point: { + uuid: string; + position: [number, number, number]; + actions: { uuid: string; name: string; type: string; start: { x: number, y: number } | {}, hitCount: number, end: { x: number, y: number } | {}, buffer: number }; + connections: { source: { pathUUID: string; pointUUID: string }; targets: { pathUUID: string; pointUUID: string }[] }; + speed: number; + }; + }; }; // Array of floor items for managing multiple objects on the floor From f78b7ce6c5785fcd0b48e02506fe4fb5c98c558d Mon Sep 17 00:00:00 2001 From: Gomathi9520 Date: Mon, 31 Mar 2025 19:39:09 +0530 Subject: [PATCH 10/11] conflicts resolved in api file --- app/src/components/ui/componets/Dropped3dWidget.tsx | 1 - app/src/components/ui/componets/RealTimeVisulization.tsx | 3 +++ app/src/pages/Project.tsx | 2 +- app/src/services/realTimeVisulization/zoneData/addWidgets.ts | 4 ++-- .../services/realTimeVisulization/zoneData/getFloatingData.ts | 4 ++-- app/src/services/realTimeVisulization/zoneData/getZoneData.ts | 4 ++-- app/src/services/realTimeVisulization/zoneData/panel.ts | 4 ++-- 7 files changed, 12 insertions(+), 10 deletions(-) diff --git a/app/src/components/ui/componets/Dropped3dWidget.tsx b/app/src/components/ui/componets/Dropped3dWidget.tsx index c627af4..94c5e45 100644 --- a/app/src/components/ui/componets/Dropped3dWidget.tsx +++ b/app/src/components/ui/componets/Dropped3dWidget.tsx @@ -26,7 +26,6 @@ export default function Dropped3dWidgets() { Record >({}); const { setWidgets3D } = use3DWidget() - useEffect(() => { if (activeModule !== "visualization") return if (selectedZone.zoneName === "") return; diff --git a/app/src/components/ui/componets/RealTimeVisulization.tsx b/app/src/components/ui/componets/RealTimeVisulization.tsx index 3f81518..f6a041a 100644 --- a/app/src/components/ui/componets/RealTimeVisulization.tsx +++ b/app/src/components/ui/componets/RealTimeVisulization.tsx @@ -63,6 +63,7 @@ const RealTimeVisulization: React.FC = () => { const organization = email?.split("@")[1]?.split(".")[0]; try { const response = await getZone2dData(organization); + console.log('response: ', response); if (!Array.isArray(response)) { return; @@ -152,6 +153,7 @@ const RealTimeVisulization: React.FC = () => { .getState() .setZone(selectedZone.zoneName, selectedZone.zoneId); } + // Add the dropped object to the zone if the API call is successful if (response.message === "FloatWidget created successfully") { useDroppedObjectsStore @@ -200,6 +202,7 @@ const RealTimeVisulization: React.FC = () => {
{activeModule === "visualization" && selectedZone.zoneName !== "" && } + {/* */} {activeModule === "visualization" && ( <> { return (
- {/* {loadingProgress && } */} + {loadingProgress && } {!isPlaying && ( <> diff --git a/app/src/services/realTimeVisulization/zoneData/addWidgets.ts b/app/src/services/realTimeVisulization/zoneData/addWidgets.ts index 08969ab..8539e54 100644 --- a/app/src/services/realTimeVisulization/zoneData/addWidgets.ts +++ b/app/src/services/realTimeVisulization/zoneData/addWidgets.ts @@ -1,5 +1,5 @@ -// let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`; -let url_Backend_dwinzo = `http://192.168.0.102:5000`; +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 addingWidgets = async ( zoneId: string, organization: string, diff --git a/app/src/services/realTimeVisulization/zoneData/getFloatingData.ts b/app/src/services/realTimeVisulization/zoneData/getFloatingData.ts index 0246175..80d2b19 100644 --- a/app/src/services/realTimeVisulization/zoneData/getFloatingData.ts +++ b/app/src/services/realTimeVisulization/zoneData/getFloatingData.ts @@ -1,5 +1,5 @@ -// let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`; -let url_Backend_dwinzo = `http://192.168.0.102:5000`; +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 getFloatingZoneData = async ( ZoneId?: string, organization?: string diff --git a/app/src/services/realTimeVisulization/zoneData/getZoneData.ts b/app/src/services/realTimeVisulization/zoneData/getZoneData.ts index f68ef15..efbac3b 100644 --- a/app/src/services/realTimeVisulization/zoneData/getZoneData.ts +++ b/app/src/services/realTimeVisulization/zoneData/getZoneData.ts @@ -1,5 +1,5 @@ -// let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`; -let url_Backend_dwinzo = `http://192.168.0.102:5000`; +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 getZone2dData = async (organization?: string) => { try { const response = await fetch( diff --git a/app/src/services/realTimeVisulization/zoneData/panel.ts b/app/src/services/realTimeVisulization/zoneData/panel.ts index 6d93d1f..82f1289 100644 --- a/app/src/services/realTimeVisulization/zoneData/panel.ts +++ b/app/src/services/realTimeVisulization/zoneData/panel.ts @@ -1,5 +1,5 @@ -// let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`; -let url_Backend_dwinzo = `http://192.168.0.102:5000`; +let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`; +// let url_Backend_dwinzo = `http://192.168.0.102:5000`; type Side = "top" | "bottom" | "left" | "right"; export const panelData = async ( From 945fc0396f24f04cad024ee8d7de8f388e42397c Mon Sep 17 00:00:00 2001 From: Jerald-Golden-B Date: Mon, 31 Mar 2025 19:54:21 +0530 Subject: [PATCH 11/11] No code changes detected; skipping commit. --- .../ui/componets/DroppedFloatingWidgets.tsx | 694 +++++++++--------- 1 file changed, 339 insertions(+), 355 deletions(-) diff --git a/app/src/components/ui/componets/DroppedFloatingWidgets.tsx b/app/src/components/ui/componets/DroppedFloatingWidgets.tsx index 4f2deb5..d7a6960 100644 --- a/app/src/components/ui/componets/DroppedFloatingWidgets.tsx +++ b/app/src/components/ui/componets/DroppedFloatingWidgets.tsx @@ -56,113 +56,159 @@ const DroppedObjects: React.FC = () => { ); const [offset, setOffset] = useState<[number, number] | null>(null); const { setSelectedChartId } = useWidgetStore(); - const positionRef = useRef<[number, number] | null>(null); + const [activeEdges, setActiveEdges] = useState<{ + vertical: "top" | "bottom"; + horizontal: "left" | "right"; + } | null>(null); // State to track active edges for distance lines + const [currentPosition, setCurrentPosition] = useState<{ + top: number | "auto"; + left: number | "auto"; + right: number | "auto"; + bottom: number | "auto"; + } | null>(null); // State to track the current position during drag const animationRef = useRef(null); const { activeModule } = useModuleStore(); - // Get the first zone and its objects + // Clean up animation frame on unmount + useEffect(() => { + return () => { + if (animationRef.current) { + cancelAnimationFrame(animationRef.current); + } + }; + }, []); + const zoneEntries = Object.entries(zones); - if (zoneEntries.length === 0) return null; // No zone, nothing to render - const [zoneName, zone] = zoneEntries[0]; // Only render the first zone - console.log("zone", zone); + if (zoneEntries.length === 0) return null; + const [zoneName, zone] = zoneEntries[0]; + + function handleDuplicate(zoneName: string, index: number) { + setOpenKebabId(null) + duplicateObject(zoneName, index); // Call the duplicateObject method from the store + } + + async function handleDelete(zoneName: string, id: string, index: number) { + try { + const email = localStorage.getItem("email") || ""; + const organization = email?.split("@")[1]?.split(".")[0]; + + let res = await deleteFloatingWidgetApi(id, organization); + console.log('res: ', res); + + if (res.message === "FloatingWidget deleted successfully") { + deleteObject(zoneName, index); // Call the deleteObject method from the store + } + } catch (error) { + console.error("Error deleting floating widget:", error); + } + } + const handlePointerDown = (event: React.PointerEvent, index: number) => { + const obj = zone.objects[index]; + const container = document.getElementById("real-time-vis-canvas"); + if (!container) return; - // Handle pointer down event - function handlePointerDown(event: React.PointerEvent, index: number) { + const rect = container.getBoundingClientRect(); + const relativeX = event.clientX - rect.left; + const relativeY = event.clientY - rect.top; - const [activeEdges, setActiveEdges] = useState<{ - vertical: "top" | "bottom"; - horizontal: "left" | "right"; - } | null>(null); // State to track active edges for distance lines - const [currentPosition, setCurrentPosition] = useState<{ - top: number | "auto"; - left: number | "auto"; - right: number | "auto"; - bottom: number | "auto"; - } | null>(null); // State to track the current position during drag - const animationRef = useRef(null); - const { activeModule } = useModuleStore(); + // Determine active properties for the initial position + const [activeProp1, activeProp2] = getActiveProperties(obj.position); - // Clean up animation frame on unmount - useEffect(() => { - return () => { - if (animationRef.current) { - cancelAnimationFrame(animationRef.current); - } - }; - }, []); + // Set active edges for distance lines + const vertical = activeProp1 === "top" ? "top" : "bottom"; + const horizontal = activeProp2 === "left" ? "left" : "right"; + setActiveEdges({ vertical, horizontal }); - const zoneEntries = Object.entries(zones); - if (zoneEntries.length === 0) return null; - const [zoneName, zone] = zoneEntries[0]; + // Store the initial position strategy and active edges + setDraggingIndex({ + zone: zoneName, + index, + initialPosition: { ...obj.position }, + }); - function handleDuplicate(zoneName: string, index: number) { - setOpenKebabId(null) - duplicateObject(zoneName, index); // Call the duplicateObject method from the store + // Calculate offset from mouse to object edges + let offsetX = 0; + let offsetY = 0; + + if (activeProp1 === "top") { + offsetY = relativeY - (obj.position.top as number); + } else { + offsetY = rect.height - relativeY - (obj.position.bottom as number); } - async function handleDelete(zoneName: string, id: string, index: number) { - try { - const email = localStorage.getItem("email") || ""; - const organization = email?.split("@")[1]?.split(".")[0]; - - let res = await deleteFloatingWidgetApi(id, organization); - console.log('res: ', res); - - if (res.message === "FloatingWidget deleted successfully") { - deleteObject(zoneName, index); // Call the deleteObject method from the store - } - } catch (error) { - console.error("Error deleting floating widget:", error); - } + if (activeProp2 === "left") { + offsetX = relativeX - (obj.position.left as number); + } else { + offsetX = rect.width - relativeX - (obj.position.right as number); } + setOffset([offsetY, offsetX]); + }; - const handlePointerDown = (event: React.PointerEvent, index: number) => { - const obj = zone.objects[index]; - const container = document.getElementById("real-time-vis-canvas"); - if (!container) return; + const handlePointerMove = (event: React.PointerEvent) => { + if (!draggingIndex || !offset) return; - const rect = container.getBoundingClientRect(); - const relativeX = event.clientX - rect.left; - const relativeY = event.clientY - rect.top; + const container = document.getElementById("real-time-vis-canvas"); + if (!container) return; - // Determine active properties for the initial position - const [activeProp1, activeProp2] = getActiveProperties(obj.position); + const rect = container.getBoundingClientRect(); + const relativeX = event.clientX - rect.left; + const relativeY = event.clientY - rect.top; - // Set active edges for distance lines - const vertical = activeProp1 === "top" ? "top" : "bottom"; - const horizontal = activeProp2 === "left" ? "left" : "right"; - setActiveEdges({ vertical, horizontal }); + // Dynamically determine the current position strategy + const newPositionStrategy = determinePosition(rect, relativeX, relativeY); + const [activeProp1, activeProp2] = getActiveProperties(newPositionStrategy); - // Store the initial position strategy and active edges - setDraggingIndex({ - zone: zoneName, - index, - initialPosition: { ...obj.position }, - }); + // Update active edges for distance lines + const vertical = activeProp1 === "top" ? "top" : "bottom"; + const horizontal = activeProp2 === "left" ? "left" : "right"; + setActiveEdges({ vertical, horizontal }); - // Calculate offset from mouse to object edges - let offsetX = 0; - let offsetY = 0; + // Calculate new position based on the active properties + let newY = 0; + let newX = 0; - if (activeProp1 === "top") { - offsetY = relativeY - (obj.position.top as number); - } else { - offsetY = rect.height - relativeY - (obj.position.bottom as number); - } + if (activeProp1 === "top") { + newY = relativeY - offset[0]; + } else { + newY = rect.height - (relativeY + offset[0]); + } - if (activeProp2 === "left") { - offsetX = relativeX - (obj.position.left as number); - } else { - offsetX = rect.width - relativeX - (obj.position.right as number); - } + if (activeProp2 === "left") { + newX = relativeX - offset[1]; + } else { + newX = rect.width - (relativeX + offset[1]); + } - setOffset([offsetY, offsetX]); + // Apply boundaries + newX = Math.max(0, Math.min(rect.width - 50, newX)); + newY = Math.max(0, Math.min(rect.height - 50, newY)); + + // Create new position object + const newPosition = { + ...newPositionStrategy, + [activeProp1]: newY, + [activeProp2]: newX, + // Clear opposite properties + [activeProp1 === "top" ? "bottom" : "top"]: "auto", + [activeProp2 === "left" ? "right" : "left"]: "auto", }; - const handlePointerMove = (event: React.PointerEvent) => { + // Update the current position state for DistanceLines + setCurrentPosition(newPosition); + + if (!animationRef.current) { + animationRef.current = requestAnimationFrame(() => { + updateObjectPosition(zoneName, draggingIndex.index, newPosition); + animationRef.current = null; + }); + } + }; + + const handlePointerUp = async (event: React.PointerEvent) => { + try { if (!draggingIndex || !offset) return; const container = document.getElementById("real-time-vis-canvas"); @@ -172,299 +218,237 @@ const DroppedObjects: React.FC = () => { const relativeX = event.clientX - rect.left; const relativeY = event.clientY - rect.top; - // Dynamically determine the current position strategy - const newPositionStrategy = determinePosition(rect, relativeX, relativeY); - const [activeProp1, activeProp2] = getActiveProperties(newPositionStrategy); + // Only now determine the final position strategy + const finalPosition = determinePosition(rect, relativeX, relativeY); + const [activeProp1, activeProp2] = getActiveProperties(finalPosition); - // Update active edges for distance lines - const vertical = activeProp1 === "top" ? "top" : "bottom"; - const horizontal = activeProp2 === "left" ? "left" : "right"; - setActiveEdges({ vertical, horizontal }); - - // Calculate new position based on the active properties - let newY = 0; - let newX = 0; + // Calculate final position using the new strategy + let finalY = 0; + let finalX = 0; if (activeProp1 === "top") { - newY = relativeY - offset[0]; + finalY = relativeY - offset[0]; } else { - newY = rect.height - (relativeY + offset[0]); + finalY = rect.height - (relativeY + offset[0]); } if (activeProp2 === "left") { - newX = relativeX - offset[1]; + finalX = relativeX - offset[1]; } else { - newX = rect.width - (relativeX + offset[1]); + finalX = rect.width - (relativeX + offset[1]); } // Apply boundaries - newX = Math.max(0, Math.min(rect.width - 50, newX)); - newY = Math.max(0, Math.min(rect.height - 50, newY)); + finalX = Math.max(0, Math.min(rect.width - 50, finalX)); + finalY = Math.max(0, Math.min(rect.height - 50, finalY)); - // Create new position object - const newPosition = { - ...newPositionStrategy, - [activeProp1]: newY, - [activeProp2]: newX, - // Clear opposite properties - [activeProp1 === "top" ? "bottom" : "top"]: "auto", - [activeProp2 === "left" ? "right" : "left"]: "auto", + const boundedPosition = { + ...finalPosition, + [activeProp1]: finalY, + [activeProp2]: finalX, }; - // Update the current position state for DistanceLines - setCurrentPosition(newPosition); + // Save to backend + const email = localStorage.getItem("email") || ""; + const organization = email?.split("@")[1]?.split(".")[0]; + const response = await addingFloatingWidgets(zone.zoneId, organization, { + ...zone.objects[draggingIndex.index], + position: boundedPosition, + }); - if (!animationRef.current) { - animationRef.current = requestAnimationFrame(() => { - updateObjectPosition(zoneName, draggingIndex.index, newPosition); - animationRef.current = null; - }); + if (response.message === "Widget updated successfully") { + updateObjectPosition(zoneName, draggingIndex.index, boundedPosition); } - }; - const handlePointerUp = async (event: React.PointerEvent) => { - try { - if (!draggingIndex || !offset) return; - - const container = document.getElementById("real-time-vis-canvas"); - if (!container) return; - - const rect = container.getBoundingClientRect(); - const relativeX = event.clientX - rect.left; - const relativeY = event.clientY - rect.top; - - // Only now determine the final position strategy - const finalPosition = determinePosition(rect, relativeX, relativeY); - const [activeProp1, activeProp2] = getActiveProperties(finalPosition); - - // Calculate final position using the new strategy - let finalY = 0; - let finalX = 0; - - if (activeProp1 === "top") { - finalY = relativeY - offset[0]; - } else { - finalY = rect.height - (relativeY + offset[0]); - } - - if (activeProp2 === "left") { - finalX = relativeX - offset[1]; - } else { - finalX = rect.width - (relativeX + offset[1]); - } - - // Apply boundaries - finalX = Math.max(0, Math.min(rect.width - 50, finalX)); - finalY = Math.max(0, Math.min(rect.height - 50, finalY)); - - const boundedPosition = { - ...finalPosition, - [activeProp1]: finalY, - [activeProp2]: finalX, - }; - - // Save to backend - const email = localStorage.getItem("email") || ""; - const organization = email?.split("@")[1]?.split(".")[0]; - const response = await addingFloatingWidgets(zone.zoneId, organization, { - ...zone.objects[draggingIndex.index], - position: boundedPosition, - }); - - if (response.message === "Widget updated successfully") { - updateObjectPosition(zoneName, draggingIndex.index, boundedPosition); - } - - // Clean up - setDraggingIndex(null); - setOffset(null); - setActiveEdges(null); // Clear active edges - setCurrentPosition(null); // Reset current position - if (animationRef.current) { - cancelAnimationFrame(animationRef.current); - animationRef.current = null; - } - } catch (error) { - console.error("Error in handlePointerUp:", error); + // Clean up + setDraggingIndex(null); + setOffset(null); + setActiveEdges(null); // Clear active edges + setCurrentPosition(null); // Reset current position + if (animationRef.current) { + cancelAnimationFrame(animationRef.current); + animationRef.current = null; } - }; - - const handleKebabClick = (id: string, event: React.MouseEvent) => { - event.stopPropagation(); - setOpenKebabId((prevId) => (prevId === id ? null : id)); - }; - - const renderObjectContent = (obj: any) => { - switch (obj.className) { - case "floating total-card": - return ( - <> -
-
{obj.header}
-
-
{obj.value}
-
{obj.per}
-
-
-
- -
- - ); - case "warehouseThroughput floating": - return ( - <> -
-

Warehouse Throughput

-

- (+5) more in 2025 -

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

Fleet Efficiency

-
-
-
-
-
-
-
-
- 0% -
-
{obj.per}%
-
Optimal
-
- 100% -
- - ); - default: - return null; - } - }; - - return ( -
- {zone.objects.map((obj, index) => ( -
handlePointerDown(event, index)} - onClick={() => { - setSelectedChartId(obj) - }} - > - {obj.className === "floating total-card" ? ( - <> - - - ) : obj.className === "warehouseThroughput floating" ? ( - <> - - - ) : obj.className === "fleetEfficiency floating" ? ( - <> - - - ) : null} - {renderObjectContent(obj)} -
handleKebabClick(obj.id, event)} - > - -
- {openKebabId === obj.id && ( -
-
{ - event.stopPropagation(); - handleDuplicate(zoneName, index); // Call the duplicate handler - }}> -
- -
-
Duplicate
-
-
{ - event.stopPropagation(); - handleDelete(zoneName, obj.id, index); // Call the delete handler - }}> -
- -
-
Delete
-
-
- )} -
- ))} - - {/* Render DistanceLines component during drag */} - {draggingIndex !== null && - activeEdges !== null && - currentPosition !== null && ( - - )} -
- ); + } catch (error) { + console.error("Error in handlePointerUp:", error); + } }; -} + + const handleKebabClick = (id: string, event: React.MouseEvent) => { + event.stopPropagation(); + setOpenKebabId((prevId) => (prevId === id ? null : id)); + }; + + const renderObjectContent = (obj: any) => { + switch (obj.className) { + case "floating total-card": + return ( + <> +
+
{obj.header}
+
+
{obj.value}
+
{obj.per}
+
+
+
+ +
+ + ); + case "warehouseThroughput floating": + return ( + <> +
+

Warehouse Throughput

+

+ (+5) more in 2025 +

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

Fleet Efficiency

+
+
+
+
+
+
+
+
+ 0% +
+
{obj.per}%
+
Optimal
+
+ 100% +
+ + ); + default: + return null; + } + }; + + return ( +
+ {zone.objects.map((obj, index) => ( +
handlePointerDown(event, index)} + onClick={() => { + setSelectedChartId(obj) + }} + > + {obj.className === "floating total-card" ? ( + <> + + + ) : obj.className === "warehouseThroughput floating" ? ( + <> + + + ) : obj.className === "fleetEfficiency floating" ? ( + <> + + + ) : null} + {renderObjectContent(obj)} +
handleKebabClick(obj.id, event)} + > + +
+ {openKebabId === obj.id && ( +
+
{ + event.stopPropagation(); + handleDuplicate(zoneName, index); // Call the duplicate handler + }}> +
+ +
+
Duplicate
+
+
{ + event.stopPropagation(); + handleDelete(zoneName, obj.id, index); // Call the delete handler + }}> +
+ +
+
Delete
+
+
+ )} +
+ ))} + + {/* Render DistanceLines component during drag */} + {draggingIndex !== null && + activeEdges !== null && + currentPosition !== null && ( + + )} +
+ ); +}; export default DroppedObjects;