From 4b421c3162e0510edecaddc734d19f70795d5104 Mon Sep 17 00:00:00 2001 From: Vishnu Date: Thu, 27 Mar 2025 17:24:36 +0530 Subject: [PATCH 01/23] before merge commit --- app/src/components/ui/componets/DisplayZone.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/app/src/components/ui/componets/DisplayZone.tsx b/app/src/components/ui/componets/DisplayZone.tsx index 9f1028f..a0f6574 100644 --- a/app/src/components/ui/componets/DisplayZone.tsx +++ b/app/src/components/ui/componets/DisplayZone.tsx @@ -140,7 +140,12 @@ const DisplayZone: React.FC = ({ }; return ( -
+
{/* Left Arrow */} {showLeftArrow && ( {open && (
diff --git a/app/src/components/ui/menu/menu.tsx b/app/src/components/ui/menu/menu.tsx new file mode 100644 index 0000000..b374237 --- /dev/null +++ b/app/src/components/ui/menu/menu.tsx @@ -0,0 +1,604 @@ +import React, { useState } from "react"; + +const MenuBar = () => { + const [activeMenu, setActiveMenu] = useState(null); + const [activeSubMenu, setActiveSubMenu] = useState(null); + + // State to track selection for all menu items + const [selectedItems, setSelectedItems] = useState>( + {} + ); + + // Function to toggle selection for a specific item + const toggleSelection = (itemName: string) => { + setSelectedItems((prev) => ({ + ...prev, + [itemName]: !prev[itemName], // Toggle the selection state + })); + }; + + return ( +
+ {/* Top-level menu buttons */} +
+ {/* File Menu */} +
setActiveMenu("File")} + onMouseLeave={() => { + setActiveMenu(null); + setActiveSubMenu(null); + }} + > +
+ File + +
+ + {/* File Dropdown */} + {activeMenu === "File" && ( +
+ {/* New File */} +
toggleSelection("New File")} + > +
+ + {selectedItems["New File"] && "✓ "} + New File + +
+ Ctrl + N +
+
+
+ + {/* Open Local File */} +
toggleSelection("Open Local File")} + > +
+ + {selectedItems["Open Local File"] && "✓ "} + Open Local File + +
+ Ctrl + O +
+
+
+ + {/* Save Version */} +
toggleSelection("Save Version")} + > +
+ + {selectedItems["Save Version"] && "✓ "} + Save Version + +
+
+
+ + {/* Make a Copy */} +
toggleSelection("Make a Copy")} + > +
+ + {selectedItems["Make a Copy"] && "✓ "} + Make a Copy + +
+
+ + {/* Share */} +
toggleSelection("Share")} + > +
+ + {selectedItems["Share"] && "✓ "} + Share + +
+
+ + {/* Rename */} +
toggleSelection("Rename")} + > +
+ + {selectedItems["Rename"] && "✓ "} + Rename + +
+
+
+ + {/* Import */} +
toggleSelection("Import")} + > +
+ + {selectedItems["Import"] && "✓ "} + Import + +
+
+ + {/* Close File */} +
toggleSelection("Close File")} + > +
+ + {selectedItems["Close File"] && "✓ "} + Close File + +
+
+
+ )} +
+ + {/* Edit Menu */} +
setActiveMenu("Edit")} + onMouseLeave={() => { + setActiveMenu(null); + setActiveSubMenu(null); + }} + > +
+ Edit + +
+ + {/* Edit Dropdown */} + {activeMenu === "Edit" && ( +
+ {/* Undo */} +
toggleSelection("Undo")} + > +
+ + {selectedItems["Undo"] && "✓ "} + Undo + +
+ Ctrl + Z +
+
+
+ + {/* Redo */} +
toggleSelection("Redo")} + > +
+ + {selectedItems["Redo"] && "✓ "} + Redo + +
+ Ctrl + Shift + Z +
+
+
+
+ + {/* Undo History */} +
toggleSelection("Undo History")} + > +
+ + {selectedItems["Undo History"] && "✓ "} + Undo History + +
+
+ + {/* Redo History */} +
toggleSelection("Redo History")} + > +
+ + {selectedItems["Redo History"] && "✓ "} + Redo History + +
+
+
+ + {/* Find */} +
toggleSelection("Find")} + > +
+ + {selectedItems["Find"] && "✓ "} + Find + +
+ Ctrl + F +
+
+
+ + {/* Delete */} +
toggleSelection("Delete")} + > +
+ + {selectedItems["Delete"] && "✓ "} + Delete + +
+
+ + {/* Select by... */} +
toggleSelection("Select by...")} + > +
+ + {selectedItems["Select by..."] && "✓ "} + Select by... + +
+
+ + {/* Keymap */} +
toggleSelection("Keymap")} + > +
+ + {selectedItems["Keymap"] && "✓ "} + Keymap + +
+
+
+ )} +
+ + {/* View Menu */} +
setActiveMenu("View")} + onMouseLeave={() => { + setActiveMenu(null); + setActiveSubMenu(null); + }} + > +
+ View + +
+ + {/* View Dropdown */} + {activeMenu === "View" && ( +
+ {/* Grid */} +
toggleSelection("Grid")} + > +
+ + {selectedItems["Grid"] && "✓ "} + Grid + +
+
+ + {/* Gizmo */} +
setActiveSubMenu("View-Gizmo")} + onMouseLeave={() => setActiveSubMenu(null)} + > +
+ + {selectedItems["Gizmo"] && "✓ "} + Gizmo + + +
+
+ + {/* Gizmo Submenu */} + {activeSubMenu === "View-Gizmo" && ( +
+ {/* Visibility */} +
toggleSelection("Visibility")} + > + + {selectedItems["Visibility"] && "✓ "} + Visibility + +
+ + {/* Cube view */} +
toggleSelection("Cube view")} + > + + {selectedItems["Cube view"] && "✓ "} + Cube view + +
+ + {/* Sphere view */} +
toggleSelection("Sphere view")} + > + + {selectedItems["Sphere view"] && "✓ "} + Sphere view + +
+ + {/* Custom settings */} +
toggleSelection("Custom settings")} + > + + {selectedItems["Custom settings"] && "✓ "} + Custom settings + +
+
+ )} +
+ + {/* Zoom */} +
toggleSelection("Zoom")} + > +
+ + {selectedItems["Zoom"] && "✓ "} + Zoom + +
+
+ + {/* Full Screen */} +
toggleSelection("Full Screen")} + > +
+ + {selectedItems["Full Screen"] && "✓ "} + Full Screen + +
+ F11 +
+
+
+
+ )} +
+ + {/* Version History Menu */} +
setActiveMenu("Version history")} + onMouseLeave={() => { + setActiveMenu(null); + setActiveSubMenu(null); + }} + > +
+ Version history +
+ + +
+ + {/* Export As Menu */} +
setActiveMenu("Export as...")} + onMouseLeave={() => { + setActiveMenu(null); + setActiveSubMenu(null); + }} + > +
+ Export as... +
+ +
+ + {/* Apps Menu */} +
setActiveMenu("Apps")} + onMouseLeave={() => { + setActiveMenu(null); + setActiveSubMenu(null); + }} + > +
+ 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 */} +
setActiveMenu("Help")} + onMouseLeave={() => { + setActiveMenu(null); + setActiveSubMenu(null); + }} + > +
+ Help + +
+ + {/* Help Dropdown */} + {activeMenu === "Help" && ( +
+ {/* Shortcuts */} +
toggleSelection("Shortcuts")} + > +
+ + {selectedItems["Shortcuts"] && "✓ "} + Shortcuts + +
+ Ctrl + Shift + ? +
+
+
+ + {/* Manual */} +
toggleSelection("Manual")} + > +
+ + {selectedItems["Manual"] && "✓ "} + Manual + +
+
+ + {/* Video Tutorials */} +
toggleSelection("Video Tutorials")} + > +
+ + {selectedItems["Video Tutorials"] && "✓ "} + Video Tutorials + +
+
+ + {/* Report a bug */} +
toggleSelection("Report a bug")} + > +
+ + {selectedItems["Report a bug"] && "✓ "} + Report a bug + +
+
+
+ )} +
+
+
+ ); +}; + +export default MenuBar; + + diff --git a/app/src/styles/base/base.scss b/app/src/styles/base/base.scss index f7bf4b0..73e602b 100644 --- a/app/src/styles/base/base.scss +++ b/app/src/styles/base/base.scss @@ -104,25 +104,34 @@ body { /* Apply custom scrollbar styles globally */ ::-webkit-scrollbar { - width: 8px; /* Width of the scrollbar */ - height: 8px; /* Height for horizontal scrollbars */ + width: 8px; + /* Width of the scrollbar */ + height: 8px; + /* Height for horizontal scrollbars */ } ::-webkit-scrollbar-track { - background: transparent; /* Background of the scrollbar track */ - border-radius: 4px; /* Rounded corners */ + background: transparent; + /* Background of the scrollbar track */ + border-radius: 4px; + /* Rounded corners */ } ::-webkit-scrollbar-thumb { - background: var(--accent-color); /* Scrollbar handle color */ - border-radius: 4px; /* Rounded corners */ - border: 2px solid #f4f4f4; /* Padding around the scrollbar handle */ + background: var(--accent-color); + /* Scrollbar handle color */ + border-radius: 4px; + /* Rounded corners */ + border: 2px solid #f4f4f4; + /* Padding around the scrollbar handle */ } ::-webkit-scrollbar-thumb:hover { - background: var(--accent-color); /* Handle color on hover */ + background: var(--accent-color); + /* Handle color on hover */ } ::-webkit-scrollbar-corner { - background: transparent; /* Remove corner styling for scrollable containers */ -} + background: transparent; + /* Remove corner styling for scrollable containers */ +} \ No newline at end of file diff --git a/app/src/styles/components/input.scss b/app/src/styles/components/input.scss index 36ac8ff..f60aca6 100644 --- a/app/src/styles/components/input.scss +++ b/app/src/styles/components/input.scss @@ -28,12 +28,14 @@ .toggle-header-container { @include flex-center; padding: 6px 12px; + .toggle-header-item { width: 100%; text-align: center; padding: 4px 12px; border-radius: #{$border-radius-large}; } + .active { background-color: var(--accent-color); color: var(--primary-color); @@ -46,6 +48,7 @@ padding: 8px 10px; background: var(--background-color); z-index: 1; + .search-container { @include flex-center; width: 100%; @@ -83,11 +86,13 @@ border: none; cursor: pointer; background-color: transparent; + &:hover { background-color: var(--highlight-accent-color); } } } + .active { border: 1px solid var(--accent-color); } @@ -96,9 +101,11 @@ .kebab-menu-container { position: relative; @include flex-center; + .kebab-icon { @include flex-center; } + .menu-list { position: absolute; left: 10px; @@ -109,6 +116,7 @@ z-index: 1; padding: 8px 4px; width: 170px; + .menu-item { margin: 2px 0; padding: 2px 4px; @@ -116,25 +124,31 @@ border-radius: #{$border-radius-small}; display: flex; gap: 2px; + &:hover { background-color: var(--background-color-secondary); } + .icon-container { @include flex-center; height: 18px; width: 18px; + path { stroke: var(--accent-color); } } } + .selected { background-color: var(--highlight-accent-color); color: var(--accent-color); + &:hover { background-color: var(--highlight-accent-color); } } + input { display: none; } @@ -144,6 +158,7 @@ .project-dropdowm-container { position: relative; height: 32px; + .project-name { line-height: 32px; height: 100%; @@ -158,6 +173,7 @@ border-radius: 6px; position: relative; cursor: pointer; + .dropdown-header { height: 100%; display: flex; @@ -180,14 +196,17 @@ left: 0; top: 110%; padding: 4px; + .dropdown-search { margin-bottom: 4px; } + .option { padding: 2px 4px; cursor: pointer; flex-direction: row !important; border-radius: #{$border-radius-small}; + &:hover { color: var(--accent-color); background-color: var(--highlight-accent-color); @@ -203,6 +222,7 @@ .input.default { width: 100%; position: relative; + .dropdown { top: 3px; right: 3px; @@ -210,6 +230,7 @@ background: var(--highlight-accent-color); border-radius: #{$border-radius-small}; padding: 1px 4px; + .active-option { color: var(--accent-color); font-size: var(--font-size-small); @@ -223,6 +244,7 @@ input { border-radius: #{$border-radius-small}; border: 1px solid var(--border-color); outline: none; + &:focus, &:active { border: 1px solid var(--accent-color); @@ -232,14 +254,17 @@ input { .eye-dropper-input-container { display: flex; align-items: center; + .label { width: 40%; } + .input-container { width: 60%; position: relative; @include flex-center; gap: 4px; + .eye-picker-button { height: 24px; min-width: 24px; @@ -248,6 +273,7 @@ input { background: var(--background-color-secondary); cursor: pointer; } + .active { background: var(--accent-color); } @@ -264,6 +290,13 @@ input { border: 1px solid var(--border-color) !important; padding: 5px 10px; + .label { + white-space: nowrap; + overflow: hidden; + max-width: 80%; + text-overflow: ellipsis; + } + // font-size: 12px; cursor: pointer; border-radius: 5px; @@ -295,9 +328,11 @@ input { display: flex; flex-direction: column; gap: 6px; + .nested-dropdown { margin-left: 0; } + padding: 10px; } @@ -306,7 +341,7 @@ input { padding: 5px 10px; text-decoration: none; color: var(--text-color); - font-size: 14px; + font-size: var(--font-size-regular); cursor: pointer; transition: background-color 0.3s ease; @@ -324,11 +359,12 @@ input { justify-content: space-between; padding: 5px 10px; cursor: pointer; - font-size: 14px; + font-size: var(--font-size-regular); color: var(--text-color); transition: background-color 0.3s ease; border-radius: #{$border-radius-small}; - .arrow-container{ + + .arrow-container { @include flex-center; } @@ -342,7 +378,7 @@ input { } .icon { - font-size: 12px; + font-size: var(--font-size-small); margin-left: 5px; } } @@ -429,6 +465,7 @@ input { outline: 3px solid var(--accent-color); outline-offset: -3px; transform: translateY(-5px); + &:hover { transform: scale(1.1) translateY(-5px); } @@ -530,6 +567,7 @@ input { .labeled-button-container { @include flex-space-between; padding: 6px 12px; + button { padding: 2px 32px; border: none; @@ -538,6 +576,7 @@ input { background: var(--accent-color); transition: all 0.2s; cursor: pointer; + &:hover { color: var(--primary-color); } @@ -548,12 +587,15 @@ input { margin-bottom: 6px; padding: 6px 12px; @include flex-space-between; + .label { width: 40%; } + .regularDropdown-container { width: 60%; } + .default { width: 60%; } @@ -562,6 +604,7 @@ input { .multi-email-invite-input-container { @include flex-space-between; gap: 20px; + .multi-email-invite-input { width: 100%; display: flex; @@ -571,24 +614,29 @@ input { flex-wrap: wrap; max-height: 180px; overflow: auto; + input { border: none; + &::placeholder { color: var(--text-disabled); } } + .entered-emails { @include flex-center; gap: 2px; background: var(--background-color-gray); padding: 0 4px; border-radius: #{$border-radius-large}; + span { height: 14px; width: 14px; line-height: 12px; text-align: center; border-radius: #{$border-radius-small}; + &:hover { background: var(--accent-color); color: var(--primary-color); @@ -596,13 +644,15 @@ input { } } } + .invite-button { padding: 4px 12px; border-radius: #{$border-radius-large}; background: var(--accent-color); color: var(--primary-color); } + .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 new file mode 100644 index 0000000..60b760f --- /dev/null +++ b/app/src/styles/components/menu/menu.scss @@ -0,0 +1,129 @@ +.menu-bar { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + z-index: 5; + background-color: var(--background-color); + color: var(--text-color); + box-shadow: var(--box-shadow-light); + border-radius: 8px; + + .menu-buttons { + display: flex; + flex-direction: column; + height: 100%; + padding: 8px 4px; + min-width: 178px; + + .menu-button-container { + position: relative; + height: 100%; + padding: 8px; + + .menu-button { + width: 100%; + cursor: pointer; + transition: all 0.2s ease; + display: flex; + align-items: center; + justify-content: space-between; + position: relative; + + .dropdown-icon { + margin-left: 5px; + font-size: var(--font-size-small); + color: #666666; + } + } + + .dropdown-menu { + position: absolute; + top: 0; + left: 100%; + background-color: var(--background-color); + min-width: 220px; + border-radius: 4px; + box-shadow: var(--box-shadow-light); + border: 1px solid var(--background-color); + z-index: 100; + padding: 5px 0; + + .menu-item-container { + position: relative; + + .menu-item { + display: flex; + justify-content: space-between; + align-items: center; + padding: 8px 20px; + cursor: pointer; + white-space: nowrap; + color: var(--text-color); + + &:hover { + background-color: var(--highlight-accent-color); + color: var(--highlight-accent-color); + } + + .menu-item-right { + display: flex; + align-items: center; + gap: 15px; + + .shortcut { + color: var(--text-color); + font-size: var(--font-size-small); + } + + .icon { + font-size: var(--font-size-small); + color: var(--text-color); + } + } + } + + .submenu { + position: absolute; + left: 100%; + top: 0; + background-color: var(--background-color); + min-width: 200px; + border-radius: 0 4px 4px 4px; + box-shadow: var(--box-shadow-light); + border: 1px solid var(--background-color); + z-index: 101; + + .submenu-item { + padding: 8px 20px; + cursor: pointer; + display: flex; + justify-content: space-between; + color: var(--text-color); + + &:hover { + background-color: var(--background-color-gray); + color: var(--highlight-accent-color); + } + + .shortcut { + color: var(--text-color); + } + } + } + } + } + + &:hover { + background-color: var(--highlight-accent-color); + color: var(--highlight-accent-color); + } + } + } +} + +.split { + width: 100%; + height: 1px; + background-color: #E0DFFF; +} \ No newline at end of file diff --git a/app/src/styles/components/visualization/floating/common.scss b/app/src/styles/components/visualization/floating/common.scss index 00c88a5..7c74d54 100644 --- a/app/src/styles/components/visualization/floating/common.scss +++ b/app/src/styles/components/visualization/floating/common.scss @@ -92,7 +92,7 @@ align-items: center; .value { - font-size: 30px; + font-size: var(--font-size-xxxlarge); color: var(--accent-color); } @@ -170,15 +170,16 @@ } } } - .bar-chart{ - padding:14px 0; + + .bar-chart { + padding: 14px 0; } } .stateWorking-wrapper { min-width: 445px; - font-size: 12px; + font-size: var(--font-size-small); backdrop-filter: blur(40px); background: var(--background-color-secondary); border-radius: 20px; @@ -195,7 +196,7 @@ flex-direction: column; span { - font-size: 27px; + font-size: var(--font-size-xxlarge); &:first-child { color: #FCFDFD; @@ -362,13 +363,13 @@ border-radius: 4px; .label { - font-size: 0.9rem; + font-size: var(--font-size-regular); margin: 0; opacity: 0.7; } .value { - font-size: 1.2rem; + font-size: var(--font-size-xlarge); font-weight: bold; margin: 0; } @@ -398,19 +399,19 @@ text-align: center; .title { - font-size: 1rem; + font-size: var(--font-size-large); margin: 0; opacity: 0.7; } .time { - font-size: 2.5rem; + font-size: var(--font-size-xxxlarge); font-weight: bold; margin: 0; } .subtitle { - font-size: 0.8rem; + font-size: var(--font-size-regular); margin: 0; opacity: 0.7; } @@ -461,7 +462,7 @@ display: flex; justify-content: space-between; color: #718096; - font-size: 12px; + font-size: var(--font-size-small); padding: 18px 10px; position: relative; z-index: 100; diff --git a/app/src/styles/components/visualization/floating/energyConsumed.scss b/app/src/styles/components/visualization/floating/energyConsumed.scss index a5b9cee..5ef39de 100644 --- a/app/src/styles/components/visualization/floating/energyConsumed.scss +++ b/app/src/styles/components/visualization/floating/energyConsumed.scss @@ -55,7 +55,7 @@ } .state { - font-size: 24px; + font-size: var(--font-size-xxlarge); font-weight: bold; } @@ -66,7 +66,7 @@ } .working { - font-size: 20px; + font-size: var(--font-size-xxlarge); color: #4CAF50; } @@ -95,7 +95,7 @@ display: flex; justify-content: space-between; align-items: center; - font-size: 16px; + font-size: var(--font-size-large); padding: 4px 0; } diff --git a/app/src/styles/components/visualization/ui/styledWidgets.scss b/app/src/styles/components/visualization/ui/styledWidgets.scss index 9c72fae..794b756 100644 --- a/app/src/styles/components/visualization/ui/styledWidgets.scss +++ b/app/src/styles/components/visualization/ui/styledWidgets.scss @@ -25,7 +25,7 @@ var(--highlight-accent-color) 60%, transparent ); - font-size: 16px; + font-size: var(--font-size-large); align-items: center; } .stock-item { @@ -36,7 +36,7 @@ color: var(--accent-color); } .stock-description { - font-size: 12px; + font-size: var(--font-size-small); } } } diff --git a/app/src/styles/layout/sidebar.scss b/app/src/styles/layout/sidebar.scss index b248ad2..228a44c 100644 --- a/app/src/styles/layout/sidebar.scss +++ b/app/src/styles/layout/sidebar.scss @@ -10,6 +10,7 @@ border-radius: #{$border-radius-extra-large}; box-shadow: #{$box-shadow-medium}; z-index: #{$z-index-tools}; + .header-container { @include flex-space-between; padding: 10px; @@ -75,6 +76,7 @@ .widget2D { overflow: auto; + .chart-container { display: flex; flex-direction: column; @@ -122,18 +124,19 @@ .value { color: var(--accent-color); - font-size: 16px; + font-size: var(--font-size-large); } } .stock-description { - font-size: 12px; + font-size: var(--font-size-small); } } } } } } + .widget3D { display: flex; flex-direction: column; @@ -232,6 +235,7 @@ display: flex; width: 100%; justify-content: flex-end; + .other-guest { @include flex-center; height: 26px; @@ -327,7 +331,7 @@ align-items: center; .multi-level-dropdown { - min-width: 170px; + width: 170px; .dropdown-button { display: flex; @@ -359,6 +363,7 @@ border-bottom: 1px solid var(--border-color); padding-bottom: 6px; } + .inputs-wrapper { display: flex; flex-direction: column; @@ -393,12 +398,12 @@ box-shadow: none; color: #5273eb; padding: 6px; - font-size: 18px; + font-size: var(--font-size-xlarge); } .bulletPoint { color: #5273eb; - font-size: 16px; + font-size: var(--font-size-large); } .regularDropdown-container { @@ -422,7 +427,7 @@ gap: 6px; color: #444; border-radius: 6px; - font-size: 14px; + font-size: var(--font-weight-regular); .infoIcon { padding: 0px 7px; @@ -442,7 +447,7 @@ flex-direction: column; gap: 15px; padding: 0; - font-size: 14px; + font-size: var(--font-weight-regular); color: #4a4a4a; .selectedWidget { @@ -508,6 +513,7 @@ } } } + .machine-mechanics-container { .machine-mechanics-header { padding: 8px 12px; @@ -515,15 +521,18 @@ border-bottom: 1px solid var(--border-color); color: var(--accent-color); } + .process-list-container { display: flex; align-items: center; gap: 4px; padding: 8px; border-bottom: 1px solid var(--border-color); + .label { margin-right: 8px; } + .add-new-process { @include flex-center; height: 24px; @@ -531,12 +540,15 @@ cursor: pointer; background: var(--background-color-secondary); border-radius: #{$border-radius-medium}; + path { stroke: var(--accent-color); strokeWidth: 1.5px; } + &:hover { background: var(--accent-color); + path { stroke: var(--highlight-accent-color); } @@ -544,11 +556,13 @@ } } } + .machine-mechanics-content-container, .simulations-container { max-height: calc(60vh - (47px - 35px)); overflow: auto; overflow-y: scroll; + .header { @include flex-space-between; padding: 6px 12px; @@ -588,17 +602,20 @@ margin: 2px 0; border-radius: #{$border-radius-small}; } + .value { display: flex; justify-content: flex-start; align-items: center; min-width: 80%; gap: 6px; + input { width: fit-content; accent-color: var(--accent-color); } } + .active { background: var(--highlight-accent-color); @@ -618,8 +635,10 @@ cursor: pointer; border-radius: #{$border-radius-small}; transform: translateX(4px); + &:hover { background-color: var(--accent-color); + path { stroke: var(--primary-color); } @@ -640,21 +659,26 @@ .selected-properties-container { padding: 12px; + .properties-header { color: var(--accent-color); font-weight: var(--font-weight-regular); padding: 8px 0; } + .value-field-container { margin-bottom: 6px; padding: 0; @include flex-space-between; + .label { width: 40%; } + .regularDropdown-container { width: 60%; } + .default { width: 60%; } @@ -668,23 +692,29 @@ margin-top: 8px; border-top: 1px solid var(--border-color); border-bottom: 1px solid var(--border-color); + .header { color: var(--accent-color); } } + .process-container { padding: 0 12px; margin: 6px 0; + .value { @include flex-space-between; + .arrow-container { height: 16px; width: 16px; } + .active { rotate: 90deg; } } + .children-drop { .value { padding: 6px; @@ -707,20 +737,25 @@ background: var(--background-color-gray); padding: 12px; border-radius: #{$border-radius-medium}; + .compare-simulations-header { font-weight: var(--font-weight-medium); } + .content { padding: 12px 0; font-size: var(--font-size-small); + span { font-size: inherit; color: var(--accent-color); } } + .input { display: flex; flex-direction: row-reverse; + input { width: fit-content; background: var(--accent-color); @@ -731,6 +766,7 @@ } } } + .global-properties-container, .analysis-main-container, .asset-properties-container, @@ -741,26 +777,32 @@ border-top: 1px solid var(--highlight-accent-color); border-bottom: 1px solid var(--highlight-accent-color); color: var(--accent-color); + .input-value { color: inherit; } } + .input-container { @include flex-center; + .remove-button { @include flex-center; height: 18px; width: 18px; margin-bottom: 6px; border-radius: 8px 0 0 8px; + &:hover { background-color: var(--accent-color); + path { stroke: var(--primary-color); } } } } + .optimize-button, .generate-report-button, .button-save { @@ -775,89 +817,109 @@ font-size: var(--font-size-small); margin-bottom: 8px; } + .split { height: 1px; background: var(--highlight-accent-color); margin: 8px; } + .custom-input-container { .header { @include flex-space-between; border: none; + .eyedrop-button { @include flex-center; } } + .inputs-container { @include flex-space-between; padding-bottom: 8px; + .input-container { padding: 0 12px; margin-top: 6px; gap: 6px; } } + .custom-input-label { white-space: nowrap; } } + .analysis-content-container { min-height: 50vh; max-height: 60vh; overflow-y: auto; + .dropdown-header-container, .dropdown-content-container { padding: 6px 12px; border-top: 1px solid var(--highlight-accent-color); } + .input-range-container { .input-container { width: 75%; } } } + .buttons-container { @include flex-space-between; padding: 12px; gap: 12px; + input { border: none; cursor: pointer; transition: all 0.2s; + &:hover { transform: translateY(-2px); box-shadow: #{$box-shadow-medium}; outline: 1px solid var(--accent-color); } } + .cancel { background: transparent; color: var(--accent-color); } + .submit { background: var(--accent-color); color: var(--highlight-accent-color); } } + .create-custom-analysis-container { margin: 6px; background: var(--background-color-gray); padding: 12px; border-radius: #{$border-radius-medium}; + .custom-analysis-header { font-weight: var(--font-weight-medium); } + .content { padding: 12px 0; font-size: var(--font-size-small); + span { font-size: inherit; color: var(--accent-color); } } + .input { display: flex; flex-direction: row-reverse; + input { width: fit-content; background: var(--accent-color); @@ -869,3 +931,122 @@ } } } + + + +.assets-container { + padding: 0 6px; + + .assets-wrapper { + position: relative; + margin: 8px 10px; + + h2 { + color: var(--text-color); + font-family: $large; + font-weight: $bold-weight ; + } + + .categories-container { + width: 100%; + display: flex; + flex-direction: row; + flex-wrap: wrap; + height: 100%; + gap: 3px; + padding: 10px 0; + + .category { + width: 117px; + height: 95px; + border-radius: 3.59px; + background-color: var(--background-color-gray); + padding: 8px; + padding-top: 12px; + font-weight: $bold-weight; + position: relative; + overflow: hidden; + + .category-name { + position: relative; + z-index: 3; + font-size: var(--font-size-large); + // -webkit-text-fill-color: transparent; + // -webkit-text-stroke: 1px black; + } + + &::after { + content: ''; + width: 50px; + height: 50px; + border-radius: 50%; + background-color: var(--circle-color, #000); + position: absolute; + top: 50%; + right: -10px; + transform: translate(0, -50%); + background: linear-gradient(144.19deg, #F1E7CD 16.62%, #FFFAEF 85.81%); + } + + .category-image { + + position: absolute; + // top: 50%; + bottom: 0; + right: -10px; + transform: translate(0, 0%) scale(0.8); + z-index: 2; + height: 80%; + } + } + } + + .assets-container { + width: 100%; + display: flex; + flex-direction: row; + flex-wrap: wrap; + height: 100%; + gap: 3px; + padding: 10px 0; + + .assets { + + width: 117px; + height: 95px; + border-radius: 3.59px; + background-color: var(--background-color-gray); + padding: 8px; + padding-top: 12px; + font-weight: $medium-weight ; + position: relative; + overflow: hidden; + + + + .asset-name { + position: relative; + z-index: 3; + font-size: var(--font-size-regular); + } + + .asset-image { + position: absolute; + top: 50%; + right: 5px; + transform: translate(0, -50%); + z-index: 2; + } + } + } + + .back-button { + position: absolute; + top: 0; + right: 0; + @include flex-center; + cursor: pointer; + } + + } +} \ No newline at end of file diff --git a/app/src/styles/main.scss b/app/src/styles/main.scss index 34c0074..a0ce207 100644 --- a/app/src/styles/main.scss +++ b/app/src/styles/main.scss @@ -23,6 +23,7 @@ @use 'components/visualization/floating/common'; @use 'components/marketPlace/marketPlace'; @use 'components/simulation/simulation'; +@use 'components/menu/menu'; // layout @use 'layout/loading'; diff --git a/app/src/styles/pages/realTimeViz.scss b/app/src/styles/pages/realTimeViz.scss index 020d6b7..69df460 100644 --- a/app/src/styles/pages/realTimeViz.scss +++ b/app/src/styles/pages/realTimeViz.scss @@ -15,6 +15,7 @@ border-radius: #{$border-radius-medium}; transition: all 0.2s; z-index: #{$z-index-default}; + .floating { width: 100%; max-width: 250px; @@ -27,6 +28,7 @@ position: absolute; z-index: 1000; } + .scene-container { overflow: hidden; } @@ -76,16 +78,19 @@ gap: 6px; border-radius: #{$border-radius-medium}; overflow-x: auto; + &::-webkit-scrollbar { display: none; } } + .no-zone { @include flex-center; gap: 4px; padding: 4px; color: var(--text-disabled); } + .zone { width: auto; background-color: var(--background-color); @@ -186,7 +191,7 @@ height: 25% !important; min-height: 150px; max-height: 100%; - border: 1px dotted #a9a9a9; + border: 1px dashed #a9a9a9; border-radius: 8px; box-shadow: 0px 2px 6px 0px rgba(60, 60, 67, 0.1); padding: 6px 0; @@ -206,8 +211,8 @@ .kebab-options { position: absolute; - top: 12px; - right: -100px; + top: 18px; + right: 5px; transform: translate(0px, 0); background-color: var(--background-color); z-index: 10; @@ -257,6 +262,10 @@ } } + .chart-container.notLinked { + border-color: red; + } + .close-btn { position: absolute; top: 5px; @@ -364,19 +373,24 @@ border: none; color: var(--background-color); border-radius: 4px; + .add-icon { @include flex-center; transition: rotate 0.2s; } + path { stroke: var(--primary-color); stroke-width: 2; } } + .active { background: #ffe3e0; + .add-icon { rotate: 45deg; + path { stroke: #f65648; stroke-width: 2; @@ -442,7 +456,7 @@ transform: translate(-0%, 0); h2 { - font-size: 12px; + font-size: var(--font-size-small); margin-bottom: 8px; color: #2b3344; } @@ -527,4 +541,4 @@ .zone.active { background-color: #007bff; color: white; -} +} \ No newline at end of file From 5564eecf76660f0e34202e4659448111e96c6886 Mon Sep 17 00:00:00 2001 From: Jerald-Golden-B Date: Sat, 29 Mar 2025 18:14:29 +0530 Subject: [PATCH 17/23] refactor: update VehicleMechanics to use LabledDropdown for start and end point selection; clean up unused MQTT code and improve zone data fetching logic --- .../mechanics/VehicleMechanics.tsx | 47 ++-- .../ui/componets/RealTimeVisulization.tsx | 4 +- app/src/components/ui/inputs/EyeDropInput.tsx | 8 +- app/src/modules/builder/agv/agv.tsx | 16 +- .../builder/geomentries/roofs/hideRoof.ts | 3 +- .../modules/simulation/path/pathConnector.tsx | 261 ++++++++++++++---- .../factoryBuilder/mqtt/mqttEvents.ts | 48 ++-- .../zoneData/getSelect2dZoneData.ts | 1 - 8 files changed, 279 insertions(+), 109 deletions(-) diff --git a/app/src/components/layout/sidebarRight/mechanics/VehicleMechanics.tsx b/app/src/components/layout/sidebarRight/mechanics/VehicleMechanics.tsx index a81ce15..c611a13 100644 --- a/app/src/components/layout/sidebarRight/mechanics/VehicleMechanics.tsx +++ b/app/src/components/layout/sidebarRight/mechanics/VehicleMechanics.tsx @@ -1,9 +1,9 @@ import React, { useRef, useMemo } from "react"; import { InfoIcon } from "../../../icons/ExportCommonIcons"; import InputWithDropDown from "../../../ui/inputs/InputWithDropDown"; -import EyeDropInput from "../../../ui/inputs/EyeDropInput"; import { useSelectedActionSphere, useSimulationPaths } from "../../../../store/store"; import * as Types from '../../../../types/world/worldTypes'; +import LabledDropdown from "../../../ui/inputs/LabledDropdown"; const VehicleMechanics: React.FC = () => { const { selectedActionSphere } = useSelectedActionSphere(); @@ -11,17 +11,31 @@ const VehicleMechanics: React.FC = () => { const propertiesContainerRef = useRef(null); - const selectedPoint = useMemo(() => { - if (!selectedActionSphere?.point?.uuid) return null; + const { selectedPoint, connectedPointUuids } = useMemo(() => { + if (!selectedActionSphere?.point?.uuid) return { selectedPoint: null, connectedPointUuids: [] }; const vehiclePaths = simulationPaths.filter( (path): path is Types.VehicleEventsSchema => path.type === "Vehicle" ); - return vehiclePaths.find( + const point = vehiclePaths.find( (path) => path.point.uuid === selectedActionSphere.point.uuid )?.point; - }, [selectedActionSphere, simulationPaths, selectedActionSphere?.point?.uuid]); + + if (!point) return { selectedPoint: null, connectedPointUuids: [] }; + + const connectedUuids: string[] = []; + if (point.connections?.targets) { + point.connections.targets.forEach(target => { + connectedUuids.push(target.pointUUID); + }); + } + + return { + selectedPoint: point, + connectedPointUuids: connectedUuids + }; + }, [selectedActionSphere, simulationPaths]); const handleActionUpdate = React.useCallback((updatedAction: Partial) => { if (!selectedActionSphere?.point?.uuid) return; @@ -45,12 +59,12 @@ const VehicleMechanics: React.FC = () => { setSimulationPaths(updatedPaths); }, [selectedActionSphere?.point?.uuid, simulationPaths, setSimulationPaths]); - const handleStartPointChange = React.useCallback((position: string) => { - handleActionUpdate({ start: position }); + const handleStartPointChange = React.useCallback((uuid: string) => { + handleActionUpdate({ start: uuid }); }, [handleActionUpdate]); - const handleEndPointChange = React.useCallback((position: string) => { - handleActionUpdate({ end: position }); + const handleEndPointChange = React.useCallback((uuid: string) => { + handleActionUpdate({ end: uuid }); }, [handleActionUpdate]); const handleHitCountChange = React.useCallback((hitCount: number) => { @@ -92,18 +106,20 @@ const VehicleMechanics: React.FC = () => { {selectedPoint && ( <> - - { /> )} -
diff --git a/app/src/components/ui/componets/RealTimeVisulization.tsx b/app/src/components/ui/componets/RealTimeVisulization.tsx index 27044b1..1948433 100644 --- a/app/src/components/ui/componets/RealTimeVisulization.tsx +++ b/app/src/components/ui/componets/RealTimeVisulization.tsx @@ -76,7 +76,7 @@ const RealTimeVisulization: React.FC = () => { } GetZoneData(); - }, []); // Removed `zones` from dependencies + }, [activeModule]); // Removed `zones` from dependencies useEffect(() => { @@ -97,7 +97,7 @@ const RealTimeVisulization: React.FC = () => { }; }); }, [selectedZone]); - + useEffect(() => { }, [floatingWidgets]) diff --git a/app/src/components/ui/inputs/EyeDropInput.tsx b/app/src/components/ui/inputs/EyeDropInput.tsx index d29613a..72c5ded 100644 --- a/app/src/components/ui/inputs/EyeDropInput.tsx +++ b/app/src/components/ui/inputs/EyeDropInput.tsx @@ -1,5 +1,6 @@ import React from "react"; import { EyeDroperIcon } from "../../icons/ExportCommonIcons"; +import RegularDropDown from "./RegularDropDown"; interface EyeDropInputProps { label: string; @@ -23,7 +24,12 @@ const EyeDropInput: React.FC = ({
{label}
- + {/* */} + { }} + />
diff --git a/app/src/modules/builder/agv/agv.tsx b/app/src/modules/builder/agv/agv.tsx index 749e39f..37c4f4e 100644 --- a/app/src/modules/builder/agv/agv.tsx +++ b/app/src/modules/builder/agv/agv.tsx @@ -1,24 +1,17 @@ import PolygonGenerator from "./polygonGenerator"; import { useThree } from "@react-three/fiber"; -import { useEffect, useRef, useState } from "react"; +import { useEffect, useMemo, useRef, useState } from "react"; import * as THREE from "three"; import * as Types from "../../../types/world/worldTypes"; import PathNavigator from "./pathNavigator"; import NavMeshDetails from "./navMeshDetails"; -const Agv = ({ - lines, - plane, -}: { - lines: Types.RefLines; - plane: Types.RefMesh; -}) => { - let pathPoints = [ +const Agv = ({ lines, plane }: { lines: Types.RefLines; plane: Types.RefMesh; }) => { + const pathPoints = useMemo(() => [ [ { x: 8.477161935339709, y: 0, z: 17.41343083550102 }, { x: 9.175416491482693, y: 0, z: -12.361001232663693 }, ], - , // [ // { x: 13.508213355232144, y: 0, z: -15.456970649652018 }, // { x: -30.464866520869617, y: 0, z: 9.779806557688929 }, @@ -27,7 +20,8 @@ const Agv = ({ { x: 16.792040856420844, y: 0, z: 15.86281907549489 }, { x: -42.77173264503395, y: 0, z: -15.821322764400804 }, ], - ]; + ], []); + let groupRef = useRef() as Types.RefGroup; const [navMesh, setNavMesh] = useState(); diff --git a/app/src/modules/builder/geomentries/roofs/hideRoof.ts b/app/src/modules/builder/geomentries/roofs/hideRoof.ts index 8eb80f8..3dc48fe 100644 --- a/app/src/modules/builder/geomentries/roofs/hideRoof.ts +++ b/app/src/modules/builder/geomentries/roofs/hideRoof.ts @@ -22,7 +22,8 @@ function hideRoof( if (roofChild.material) { const materials = Array.isArray(roofChild.material) ? roofChild.material : [roofChild.material]; materials.forEach(material => { - material.visible = v.dot(u) < 0.25; + // material.visible = v.dot(u) < 0.25; + material.visible = true; }); } } diff --git a/app/src/modules/simulation/path/pathConnector.tsx b/app/src/modules/simulation/path/pathConnector.tsx index 8df718c..0888966 100644 --- a/app/src/modules/simulation/path/pathConnector.tsx +++ b/app/src/modules/simulation/path/pathConnector.tsx @@ -86,6 +86,7 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec }; } } + // In the updatePathConnections function, modify the Vehicle handling section: else if (path.type === 'Vehicle') { // Handle outgoing connections from Vehicle if (path.modeluuid === fromPathUUID && path.point.uuid === fromPointUUID) { @@ -95,6 +96,27 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec }; const existingTargets = path.point.connections.targets || []; + // Check if we're trying to add a connection to 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"); + 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"); + return path; + } + if (!existingTargets.some(target => target.pathUUID === newTarget.pathUUID && target.pointUUID === newTarget.pointUUID @@ -119,6 +141,27 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec }; const existingTargets = path.point.connections.targets || []; + // Check if we're receiving a connection from 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"); + 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"); + return path; + } + if (!existingTargets.some(target => target.pathUUID === reverseTarget.pathUUID && target.pointUUID === reverseTarget.pointUUID @@ -135,6 +178,7 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec }; } } + return path; } return path; }); @@ -168,7 +212,6 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec drag = true; } }; - const onContextMenu = (evt: MouseEvent) => { evt.preventDefault(); if (drag || evt.button === 0) return; @@ -200,29 +243,126 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec const firstPath = simulationPaths.find(p => p.modeluuid === firstSelected?.pathUUID); const secondPath = simulationPaths.find(p => p.modeluuid === pathUUID); + // Prevent vehicle-to-vehicle connections if (firstPath && secondPath && firstPath.type === 'Vehicle' && secondPath.type === 'Vehicle') { console.log("Cannot connect two vehicle paths together"); return; } - const isAlreadyConnected = simulationPaths.some(path => { - if (path.type === 'Conveyor') { - return path.points.some(point => - point.uuid === sphereUUID && - point.connections.targets.length > 0 - ); - } else if (path.type === 'Vehicle') { - return path.point.uuid === sphereUUID && - path.point.connections.targets.length > 0; - } - return false; - }); - if (isAlreadyConnected) { - console.log("Sphere is already connected. Ignoring."); + // Prevent conveyor middle point to conveyor connections + if (firstPath && secondPath && + firstPath.type === 'Conveyor' && + secondPath.type === 'Conveyor' && + !firstSelected?.isCorner) { + console.log("Conveyor middle points can only connect to non-conveyor paths"); return; } - if (!firstSelected) { + // Check if this specific connection already exists + const isDuplicateConnection = firstSelected + ? simulationPaths.some(path => { + if (path.modeluuid === firstSelected.pathUUID) { + if (path.type === 'Conveyor') { + const point = path.points.find(p => p.uuid === firstSelected.sphereUUID); + return point?.connections.targets.some(t => + t.pathUUID === pathUUID && t.pointUUID === sphereUUID + ); + } else if (path.type === 'Vehicle') { + return path.point.connections.targets.some(t => + t.pathUUID === pathUUID && t.pointUUID === sphereUUID + ); + } + } + return false; + }) + : false; + + if (isDuplicateConnection) { + console.log("These points are already connected. Ignoring."); + return; + } + + // For Vehicles, skip the "already connected" check since they can have multiple connections + if (intersected.userData.path.type !== 'Vehicle') { + const isAlreadyConnected = simulationPaths.some(path => { + if (path.type === 'Conveyor') { + return path.points.some(point => + point.uuid === sphereUUID && + point.connections.targets.length > 0 + ); + } + return false; + }); + + if (isAlreadyConnected) { + console.log("Conveyor point is already connected. Ignoring."); + return; + } + } + + // 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"); + 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."); + return; + } + + // At least one must be start/end point + if (!firstSelected.isCorner && !isStartOrEnd) { + console.log("At least one of the selected spheres must be a start or end point."); + return; + } + + // All checks passed - make the connection + handleAddConnection( + firstSelected.pathUUID, + firstSelected.sphereUUID, + pathUUID, + sphereUUID + ); + } else { + // First selection - just store it setFirstSelected({ pathUUID, sphereUUID, @@ -230,28 +370,11 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec isCorner: isStartOrEnd }); setIsConnecting(true); - } else { - if (firstSelected.sphereUUID === sphereUUID) return; - - if (firstSelected.pathUUID === pathUUID) { - console.log("Cannot connect spheres on the same path."); - return; - } - if (!firstSelected.isCorner && !isStartOrEnd) { - console.log("At least one of the selected spheres must be a start or end point."); - return; - } - - handleAddConnection( - firstSelected.pathUUID, - firstSelected.sphereUUID, - pathUUID, - sphereUUID - ); } } } } else { + // Clicked outside - cancel connection setFirstSelected(null); setCurrentLine(null); setIsConnecting(false); @@ -294,7 +417,6 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec if (intersects.length > 0) { point = intersects[0].point; - if (point.y < 0.05) { point = new THREE.Vector3(point.x, 0.05, point.z); } @@ -316,28 +438,68 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec const secondPath = simulationPaths.find(p => p.modeluuid === pathUUID); const isVehicleToVehicle = firstPath?.type === 'Vehicle' && secondPath?.type === 'Vehicle'; + // Inside the useFrame hook, where we check for snapped spheres: const isConnectable = (pathData.type === 'Vehicle' || (pathData.points.length > 0 && ( sphereUUID === pathData.points[0].uuid || sphereUUID === pathData.points[pathData.points.length - 1].uuid - ))) && !isVehicleToVehicle; + ))) && + !isVehicleToVehicle && + !(firstPath?.type === 'Conveyor' && + pathData.type === 'Conveyor' && + !firstSelected.isCorner); - const isAlreadyConnected = simulationPaths.some(path => { - if (path.type === 'Conveyor') { - return path.points.some(point => - point.uuid === sphereUUID && - point.connections.targets.length > 0 - ); - } else if (path.type === 'Vehicle') { - return path.point.uuid === sphereUUID && - path.point.connections.targets.length > 0; + // Check for duplicate connection (regardless of path type) + const isDuplicateConnection = simulationPaths.some(path => { + if (path.modeluuid === firstSelected.pathUUID) { + if (path.type === 'Conveyor') { + const point = path.points.find(p => p.uuid === firstSelected.sphereUUID); + return point?.connections.targets.some(t => + t.pathUUID === pathUUID && t.pointUUID === sphereUUID + ); + } else if (path.type === 'Vehicle') { + return path.point.connections.targets.some(t => + t.pathUUID === pathUUID && t.pointUUID === sphereUUID + ); + } } return false; }); + // For non-Vehicle paths, check if already connected + const isNonVehicleAlreadyConnected = pathData.type !== 'Vehicle' && + simulationPaths.some(path => { + if (path.type === 'Conveyor') { + return path.points.some(point => + point.uuid === sphereUUID && + point.connections.targets.length > 0 + ); + } + return false; + }); + + // Check vehicle connection limits + 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'; + })); + if ( - !isAlreadyConnected && + !isDuplicateConnection && !isVehicleToVehicle && + !isNonVehicleAlreadyConnected && + !isVehicleAtMaxConnections && + !isVehicleConveyorConflict && firstSelected.sphereUUID !== sphereUUID && firstSelected.pathUUID !== pathUUID && (firstSelected.isCorner || isConnectable) @@ -371,13 +533,6 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec end: point, mid: midPoint, }); - console.log({ - start: firstSelected.position, - end: point, - mid: midPoint, - }); - - // setIsConnecting(true); if (sphereIntersects.length > 0) { setHelperLineColor(isInvalidConnection ? 'red' : '#6cf542'); diff --git a/app/src/services/factoryBuilder/mqtt/mqttEvents.ts b/app/src/services/factoryBuilder/mqtt/mqttEvents.ts index 41175ca..34d3939 100644 --- a/app/src/services/factoryBuilder/mqtt/mqttEvents.ts +++ b/app/src/services/factoryBuilder/mqtt/mqttEvents.ts @@ -6,37 +6,37 @@ const MqttEvents = () => { const { setTouch, setTemperature, setHumidity } = useDrieUIValue(); useEffect(() => { - const client = mqtt.connect(`ws://${process.env.REACT_APP_SERVER_MQTT_URL}`); + // const client = mqtt.connect(`ws://${process.env.REACT_APP_SERVER_MQTT_URL}`); - client.subscribe("touch"); - client.subscribe("temperature"); - client.subscribe("humidity"); + // client.subscribe("touch"); + // client.subscribe("temperature"); + // client.subscribe("humidity"); - const handleMessage = (topic: string, message: any) => { - const value = message.toString(); + // const handleMessage = (topic: string, message: any) => { + // const value = message.toString(); - if (topic === "touch") { - setTouch(value); - } else if (topic === "temperature") { - setTemperature(parseFloat(value)); - } else if (topic === "humidity") { - setHumidity(parseFloat(value)); - } - }; + // if (topic === "touch") { + // setTouch(value); + // } else if (topic === "temperature") { + // setTemperature(parseFloat(value)); + // } else if (topic === "humidity") { + // setHumidity(parseFloat(value)); + // } + // }; - client.on("message", handleMessage); + // client.on("message", handleMessage); - client.on("error", (err) => { - console.error("MQTT Connection Error:", err); - }); + // client.on("error", (err) => { + // console.error("MQTT Connection Error:", err); + // }); - client.on("close", () => { - console.log("MQTT Connection Closed"); - }); + // client.on("close", () => { + // console.log("MQTT Connection Closed"); + // }); - return () => { - client.end(); - }; + // return () => { + // client.end(); + // }; }, [setTouch, setTemperature, setHumidity]); return null; diff --git a/app/src/services/realTimeVisulization/zoneData/getSelect2dZoneData.ts b/app/src/services/realTimeVisulization/zoneData/getSelect2dZoneData.ts index 71d9c2f..26a88c5 100644 --- a/app/src/services/realTimeVisulization/zoneData/getSelect2dZoneData.ts +++ b/app/src/services/realTimeVisulization/zoneData/getSelect2dZoneData.ts @@ -1,5 +1,4 @@ let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`; -console.log("url_Backend_dwinzo: ", url_Backend_dwinzo); export const getSelect2dZoneData = async ( ZoneId?: string, From a327044b8b144aa43cb3f9f4fd9ff8c32d6ef9e5 Mon Sep 17 00:00:00 2001 From: Vishnu Date: Sat, 29 Mar 2025 18:16:18 +0530 Subject: [PATCH 18/23] refactor: simplify user profile rendering in Header component --- .../components/layout/sidebarRight/Header.tsx | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/app/src/components/layout/sidebarRight/Header.tsx b/app/src/components/layout/sidebarRight/Header.tsx index eac15e0..35517e6 100644 --- a/app/src/components/layout/sidebarRight/Header.tsx +++ b/app/src/components/layout/sidebarRight/Header.tsx @@ -42,15 +42,13 @@ const Header: React.FC = () => {
+{guestUsers.length - 3}
)} {guestUsers.slice(0, 3).map((user, index) => ( - <> -
- {user.userName[0]} -
- +
+ {user.userName[0]} +
))}
From 98eb403b4acdd813e414bb4a1c078a6a6007e046 Mon Sep 17 00:00:00 2001 From: Jerald-Golden-B Date: Sat, 29 Mar 2025 18:35:34 +0530 Subject: [PATCH 19/23] refactor: clean up code in various components; streamline material properties and improve state management --- .../components/layout/sidebarRight/Header.tsx | 16 +++++++--------- .../builder/geomentries/roofs/addRoofToScene.ts | 2 +- .../builder/geomentries/roofs/hideRoof.ts | 3 +-- app/src/modules/collaboration/collabCams.tsx | 8 +------- .../factoryBuilder/zones/deleteZoneApi.ts | 1 + 5 files changed, 11 insertions(+), 19 deletions(-) diff --git a/app/src/components/layout/sidebarRight/Header.tsx b/app/src/components/layout/sidebarRight/Header.tsx index b4983c6..46bfdaf 100644 --- a/app/src/components/layout/sidebarRight/Header.tsx +++ b/app/src/components/layout/sidebarRight/Header.tsx @@ -28,15 +28,13 @@ const Header: React.FC = () => {
+{guestUsers.length - 3}
)} {guestUsers.slice(0, 3).map((user, index) => ( - <> -
- {user.userName[0]} -
- +
+ {user.userName[0]} +
))}
diff --git a/app/src/modules/builder/geomentries/roofs/addRoofToScene.ts b/app/src/modules/builder/geomentries/roofs/addRoofToScene.ts index 090dc06..4630efc 100644 --- a/app/src/modules/builder/geomentries/roofs/addRoofToScene.ts +++ b/app/src/modules/builder/geomentries/roofs/addRoofToScene.ts @@ -17,7 +17,7 @@ function addRoofToScene( }; const geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings); - const material = new THREE.MeshStandardMaterial({ color: CONSTANTS.roofConfig.defaultColor, side: THREE.DoubleSide, transparent: true, depthWrite: false }); + const material = new THREE.MeshStandardMaterial({ color: CONSTANTS.roofConfig.defaultColor, side: THREE.DoubleSide }); const mesh = new THREE.Mesh(geometry, material); mesh.position.y = CONSTANTS.wallConfig.height + floor; mesh.castShadow = true; diff --git a/app/src/modules/builder/geomentries/roofs/hideRoof.ts b/app/src/modules/builder/geomentries/roofs/hideRoof.ts index 3dc48fe..8eb80f8 100644 --- a/app/src/modules/builder/geomentries/roofs/hideRoof.ts +++ b/app/src/modules/builder/geomentries/roofs/hideRoof.ts @@ -22,8 +22,7 @@ function hideRoof( if (roofChild.material) { const materials = Array.isArray(roofChild.material) ? roofChild.material : [roofChild.material]; materials.forEach(material => { - // material.visible = v.dot(u) < 0.25; - material.visible = true; + material.visible = v.dot(u) < 0.25; }); } } diff --git a/app/src/modules/collaboration/collabCams.tsx b/app/src/modules/collaboration/collabCams.tsx index af135a7..a699bf3 100644 --- a/app/src/modules/collaboration/collabCams.tsx +++ b/app/src/modules/collaboration/collabCams.tsx @@ -20,12 +20,7 @@ const CamModelsGroup = () => { const loader = new GLTFLoader(); const dracoLoader = new DRACOLoader(); const [cams, setCams] = useState([]); - const [models, setModels] = useState< - Record< - string, - { targetPosition: THREE.Vector3; targetRotation: THREE.Euler } - > - >({}); + const [models, setModels] = useState>({}); dracoLoader.setDecoderPath("three/examples/jsm/libs/draco/gltf/"); loader.setDRACOLoader(dracoLoader); @@ -163,7 +158,6 @@ const CamModelsGroup = () => { cam.rotation.z ); newModel.userData = cam.userData; - console.log('cam.userData: ', cam.userData); setActiveUsers([...activeUsers, cam.userData]); return newModel; }); diff --git a/app/src/services/factoryBuilder/zones/deleteZoneApi.ts b/app/src/services/factoryBuilder/zones/deleteZoneApi.ts index 2ba5156..fbe4a83 100644 --- a/app/src/services/factoryBuilder/zones/deleteZoneApi.ts +++ b/app/src/services/factoryBuilder/zones/deleteZoneApi.ts @@ -15,6 +15,7 @@ export const deleteZonesApi = async (userId: string, organization: string, zoneI } const result = await response.json(); + console.log('result: ', result); return result; } catch (error) { if (error instanceof Error) { From 3b80402fb60492de1641f9a5e4986bf294ac8410 Mon Sep 17 00:00:00 2001 From: Jerald-Golden-B Date: Sat, 29 Mar 2025 18:45:10 +0530 Subject: [PATCH 20/23] refactor: remove unnecessary whitespace in RealTimeVisualization component --- app/src/components/ui/componets/RealTimeVisulization.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/app/src/components/ui/componets/RealTimeVisulization.tsx b/app/src/components/ui/componets/RealTimeVisulization.tsx index 1948433..d84e6e8 100644 --- a/app/src/components/ui/componets/RealTimeVisulization.tsx +++ b/app/src/components/ui/componets/RealTimeVisulization.tsx @@ -162,7 +162,6 @@ const RealTimeVisulization: React.FC = () => { onDrop={(event) => handleDrop(event)} onDragOver={(event) => event.preventDefault()} > -
From 6cb8e1076ef143e2115b3c4a81ef80e13bf149f2 Mon Sep 17 00:00:00 2001 From: Vishnu Date: Sat, 29 Mar 2025 19:02:02 +0530 Subject: [PATCH 21/23] refactor: update loading styles and introduce faint gradient variables --- .../components/layout/sidebarRight/Header.tsx | 6 +- .../ui/componets/RealTimeVisulization.tsx | 64 ++--- app/src/modules/collaboration/collabCams.tsx | 6 +- app/src/styles/abstracts/variables.scss | 3 + app/src/styles/base/base.scss | 5 +- app/src/styles/components/menu/menu.scss | 230 +++++++++--------- app/src/styles/layout/loading.scss | 7 +- app/src/styles/pages/userAuth.scss | 8 +- 8 files changed, 165 insertions(+), 164 deletions(-) diff --git a/app/src/components/layout/sidebarRight/Header.tsx b/app/src/components/layout/sidebarRight/Header.tsx index 35517e6..96b526f 100644 --- a/app/src/components/layout/sidebarRight/Header.tsx +++ b/app/src/components/layout/sidebarRight/Header.tsx @@ -1,4 +1,4 @@ -import React, { useState } from "react"; +import React, { useEffect, useState } from "react"; import { AppDockIcon } from "../../icons/HeaderIcons"; import orgImg from "../../../assets/orgTemp.png"; import { useActiveUsers } from "../../../store/store"; @@ -14,6 +14,10 @@ const Header: React.FC = () => { (user: ActiveUser) => user.userName !== userName ); + useEffect(() => { + console.log(activeUsers); + }, []) + const [userManagement, setUserManagement] = useState(false); return ( diff --git a/app/src/components/ui/componets/RealTimeVisulization.tsx b/app/src/components/ui/componets/RealTimeVisulization.tsx index 346f114..500a6d9 100644 --- a/app/src/components/ui/componets/RealTimeVisulization.tsx +++ b/app/src/components/ui/componets/RealTimeVisulization.tsx @@ -13,7 +13,6 @@ import { useAsset3dWidget, useZones } from "../../../store/store"; import { getZoneData } from "../../../services/realTimeVisulization/zoneData/getZones"; import { getZone2dData } from "../../../services/realTimeVisulization/zoneData/getZoneData"; - type Side = "top" | "bottom" | "left" | "right"; type FormattedZoneData = Record< @@ -25,7 +24,7 @@ type FormattedZoneData = Record< lockedPanels: Side[]; zoneId: string; zoneViewPortTarget: number[]; - zoneViewPortPosition: number[] + zoneViewPortPosition: number[]; widgets: Widget[]; } >; @@ -45,8 +44,10 @@ const RealTimeVisulization: React.FC = () => { const [droppedObjects, setDroppedObjects] = useState([]); const [zonesData, setZonesData] = useState({}); const { selectedZone, setSelectedZone } = useSelectedZoneStore(); - const { zones } = useZones() - const [floatingWidgets, setFloatingWidgets] = useState>({}); + const { zones } = useZones(); + const [floatingWidgets, setFloatingWidgets] = useState< + Record + >({}); const { widgetSelect, setWidgetSelect } = useAsset3dWidget(); useEffect(() => { async function GetZoneData() { @@ -57,28 +58,30 @@ const RealTimeVisulization: React.FC = () => { if (!Array.isArray(response)) { return; } - const formattedData = response.reduce((acc, zone) => { - acc[zone.zoneName] = { - activeSides: [], - panelOrder: [], - lockedPanels: [], - zoneId: zone.zoneId, - zoneViewPortTarget: zone.viewPortCenter, - zoneViewPortPosition: zone.viewPortposition, - widgets: [], - }; - return acc; - }, {}); + const formattedData = response.reduce( + (acc, zone) => { + acc[zone.zoneName] = { + activeSides: [], + panelOrder: [], + lockedPanels: [], + zoneId: zone.zoneId, + zoneViewPortTarget: zone.viewPortCenter, + zoneViewPortPosition: zone.viewPortposition, + widgets: [], + }; + return acc; + }, + {} + ); setZonesData(formattedData); } catch (error) { - console.log('error: ', error); + console.log("error: ", error); } } GetZoneData(); }, []); // Removed `zones` from dependencies - useEffect(() => { setZonesData((prev) => { if (!selectedZone) return prev; @@ -97,10 +100,8 @@ const RealTimeVisulization: React.FC = () => { }; }); }, [selectedZone]); - - useEffect(() => { - }, [floatingWidgets]) + useEffect(() => {}, [floatingWidgets]); const handleDrop = (event: React.DragEvent) => { event.preventDefault(); @@ -121,24 +122,31 @@ const RealTimeVisulization: React.FC = () => { position: [relativeY, relativeX], // Y first because of top/left style }; // Only set zone if it’s not already in the store (prevents overwriting objects) - const existingZone = useDroppedObjectsStore.getState().zones[selectedZone.zoneName]; + const existingZone = + useDroppedObjectsStore.getState().zones[selectedZone.zoneName]; if (!existingZone) { - useDroppedObjectsStore.getState().setZone(selectedZone.zoneName, selectedZone.zoneId); + useDroppedObjectsStore + .getState() + .setZone(selectedZone.zoneName, selectedZone.zoneId); } // Add the dropped object to the zone - useDroppedObjectsStore.getState().addObject(selectedZone.zoneName, newObject); + useDroppedObjectsStore + .getState() + .addObject(selectedZone.zoneName, newObject); setFloatingWidgets((prevWidgets) => ({ ...prevWidgets, [selectedZone.zoneName]: { ...prevWidgets[selectedZone.zoneName], zoneName: selectedZone.zoneName, zoneId: selectedZone.zoneId, - objects: [...(prevWidgets[selectedZone.zoneName]?.objects || []), newObject], + objects: [ + ...(prevWidgets[selectedZone.zoneName]?.objects || []), + newObject, + ], }, })); }; - return (
{ width: isPlaying || activeModule !== "visualization" ? "100vw" : "", left: isPlaying || activeModule !== "visualization" ? "0%" : "", }} - >
{ onDrop={(event) => handleDrop(event)} onDragOver={(event) => event.preventDefault()} > - - +
{activeModule === "visualization" && ( diff --git a/app/src/modules/collaboration/collabCams.tsx b/app/src/modules/collaboration/collabCams.tsx index bae58c2..9f49f0c 100644 --- a/app/src/modules/collaboration/collabCams.tsx +++ b/app/src/modules/collaboration/collabCams.tsx @@ -147,6 +147,7 @@ const CamModelsGroup = () => { const filteredData = data.cameraDatas.filter( (camera: any) => camera.userData.email !== email ); + let a:any = []; if (filteredData.length > 0) { loader.load(camModel, (gltf) => { const newCams = filteredData.map((cam: any) => { @@ -163,11 +164,10 @@ const CamModelsGroup = () => { cam.rotation.z ); newModel.userData = cam.userData; - if (!activeUsers.includes(cam.userData)) { - setActiveUsers([...activeUsers, cam.userData]); - } + a.push(cam.userData); return newModel; }); + setActiveUsers(a); setCams((prev) => [...prev, ...newCams]); }); } diff --git a/app/src/styles/abstracts/variables.scss b/app/src/styles/abstracts/variables.scss index 18822b6..4d201ad 100644 --- a/app/src/styles/abstracts/variables.scss +++ b/app/src/styles/abstracts/variables.scss @@ -58,6 +58,9 @@ $acent-gradient: linear-gradient( #925df3 100% ); // Light mode accent gradient +$faint-gradient: radial-gradient(circle, #bfe0f8 0%, #e9ebff 46%, #e2acff 100%); +$faint-gradient-dark: radial-gradient(circle, #31373b 0%, #48494b 46%, #52415c 100%); + // ======================================================================== // Typography // ======================================================================== diff --git a/app/src/styles/base/base.scss b/app/src/styles/base/base.scss index 73e602b..86495f5 100644 --- a/app/src/styles/base/base.scss +++ b/app/src/styles/base/base.scss @@ -12,6 +12,7 @@ --accent-color: #{$accent-color}; // Primary accent color for light theme --highlight-accent-color: #{$highlight-accent-color}; // Highlight color for light theme --accent-gradient-color: #{$acent-gradient}; // Primary accent color for light theme + --faint-gradient-color: #{$faint-gradient}; // Background colors --background-color: #{$background-color}; // Main background color @@ -46,6 +47,7 @@ --accent-color: #{$accent-color-dark}; // Primary accent color for dark theme --highlight-accent-color: #{$highlight-accent-color-dark}; // Highlight color for dark theme --accent-gradient-color: #{$acent-gradient-dark}; // Primary accent color for light theme + --faint-gradient-color: #{$faint-gradient-dark}; // Background colors --background-color: #{$background-color-dark}; // Main background color @@ -84,7 +86,6 @@ } body { - background: var(--background-color); /* Font Sizes */ @@ -134,4 +135,4 @@ body { ::-webkit-scrollbar-corner { background: transparent; /* Remove corner styling for scrollable containers */ -} \ 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 60b760f..a4348a3 100644 --- a/app/src/styles/components/menu/menu.scss +++ b/app/src/styles/components/menu/menu.scss @@ -1,129 +1,127 @@ .menu-bar { - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - z-index: 5; - background-color: var(--background-color); - color: var(--text-color); - box-shadow: var(--box-shadow-light); - border-radius: 8px; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + z-index: 5; + background-color: var(--background-color); + color: var(--text-color); + box-shadow: var(--box-shadow-light); + border-radius: 8px; + .menu-buttons { + display: flex; + flex-direction: column; + height: 100%; + padding: 8px 4px; + min-width: 178px; - .menu-buttons { + .menu-button-container { + position: relative; + height: 100%; + padding: 8px; + + .menu-button { + width: 100%; + cursor: pointer; + transition: all 0.2s ease; display: flex; - flex-direction: column; - height: 100%; - padding: 8px 4px; - min-width: 178px; + align-items: center; + justify-content: space-between; + position: relative; - .menu-button-container { - position: relative; - height: 100%; - padding: 8px; + .dropdown-icon { + margin-left: 5px; + font-size: var(--font-size-small); + color: #666666; + } + } - .menu-button { - width: 100%; - cursor: pointer; - transition: all 0.2s ease; - display: flex; - align-items: center; - justify-content: space-between; - position: relative; + .dropdown-menu { + position: absolute; + top: 0; + left: 100%; + background-color: var(--background-color); + min-width: 220px; + border-radius: 4px; + box-shadow: var(--box-shadow-light); + border: 1px solid var(--background-color); + z-index: 100; + padding: 5px 0; - .dropdown-icon { - margin-left: 5px; - font-size: var(--font-size-small); - color: #666666; - } - } + .menu-item-container { + position: relative; - .dropdown-menu { - position: absolute; - top: 0; - left: 100%; - background-color: var(--background-color); - min-width: 220px; - border-radius: 4px; - box-shadow: var(--box-shadow-light); - border: 1px solid var(--background-color); - z-index: 100; - padding: 5px 0; - - .menu-item-container { - position: relative; - - .menu-item { - display: flex; - justify-content: space-between; - align-items: center; - padding: 8px 20px; - cursor: pointer; - white-space: nowrap; - color: var(--text-color); - - &:hover { - background-color: var(--highlight-accent-color); - color: var(--highlight-accent-color); - } - - .menu-item-right { - display: flex; - align-items: center; - gap: 15px; - - .shortcut { - color: var(--text-color); - font-size: var(--font-size-small); - } - - .icon { - font-size: var(--font-size-small); - color: var(--text-color); - } - } - } - - .submenu { - position: absolute; - left: 100%; - top: 0; - background-color: var(--background-color); - min-width: 200px; - border-radius: 0 4px 4px 4px; - box-shadow: var(--box-shadow-light); - border: 1px solid var(--background-color); - z-index: 101; - - .submenu-item { - padding: 8px 20px; - cursor: pointer; - display: flex; - justify-content: space-between; - color: var(--text-color); - - &:hover { - background-color: var(--background-color-gray); - color: var(--highlight-accent-color); - } - - .shortcut { - color: var(--text-color); - } - } - } - } - } + .menu-item { + display: flex; + justify-content: space-between; + align-items: center; + padding: 8px 20px; + cursor: pointer; + white-space: nowrap; + color: var(--text-color); &:hover { - background-color: var(--highlight-accent-color); - color: var(--highlight-accent-color); + background-color: var(--highlight-accent-color); + color: var(--highlight-accent-color); } - } - } -} -.split { + .menu-item-right { + display: flex; + align-items: center; + gap: 15px; + + .shortcut { + color: var(--text-color); + font-size: var(--font-size-small); + } + + .icon { + font-size: var(--font-size-small); + color: var(--text-color); + } + } + } + + .submenu { + position: absolute; + left: 100%; + top: 0; + background-color: var(--background-color); + min-width: 200px; + border-radius: 0 4px 4px 4px; + box-shadow: var(--box-shadow-light); + border: 1px solid var(--background-color); + z-index: 101; + + .submenu-item { + padding: 8px 20px; + cursor: pointer; + display: flex; + justify-content: space-between; + color: var(--text-color); + + &:hover { + background-color: var(--background-color-gray); + color: var(--highlight-accent-color); + } + + .shortcut { + color: var(--text-color); + } + } + } + } + } + + &:hover { + background-color: var(--highlight-accent-color); + color: var(--highlight-accent-color); + } + } + } + .split { width: 100%; height: 1px; - background-color: #E0DFFF; -} \ No newline at end of file + background-color: #e0dfff; + } +} diff --git a/app/src/styles/layout/loading.scss b/app/src/styles/layout/loading.scss index a6e3bf5..0a13753 100644 --- a/app/src/styles/layout/loading.scss +++ b/app/src/styles/layout/loading.scss @@ -18,12 +18,7 @@ &::after { content: ""; position: absolute; - background: radial-gradient( - circle, - #bfe0f8 0%, - #e9ebff 46%, - #e2acff 100% - ); + background: var(--faint-gradient-color); height: 50vh; width: 50vw; top: 0; diff --git a/app/src/styles/pages/userAuth.scss b/app/src/styles/pages/userAuth.scss index fd4048c..1bf4c7e 100644 --- a/app/src/styles/pages/userAuth.scss +++ b/app/src/styles/pages/userAuth.scss @@ -10,7 +10,6 @@ color: var(--text-color); height: 100vh; background-color: var(--background-color); - background-color: #fcfdfd; position: relative; z-index: 1; .logo-icon { @@ -168,12 +167,7 @@ &::after { content: ""; position: absolute; - background: radial-gradient( - circle, - #bfe0f8 0%, - #e9ebff 46%, - #e2acff 100% - ); + background: var(--faint-gradient-color); height: 50vh; width: 50vw; top: 0; From 6ccdb28f52a51a129d3ce03ca8a68814906f1190 Mon Sep 17 00:00:00 2001 From: Gomathi9520 Date: Sat, 29 Mar 2025 19:21:20 +0530 Subject: [PATCH 22/23] floatingwidgets Api and 3dwidget frontend and 2d widget delete Api and zonecameraUpdation --- app/package-lock.json | 48 +++- .../visualization/widgets/Widgets.tsx | 12 +- .../visualization/widgets/Widgets3D.tsx | 7 +- .../properties/ZoneProperties.tsx | 28 +- app/src/components/ui/ModuleToggle.tsx | 9 +- .../components/ui/componets/AddButtons.tsx | 8 +- .../components/ui/componets/DisplayZone.tsx | 50 ++-- .../ui/componets/DraggableWidget.tsx | 67 +++-- .../ui/componets/Dropped3dWidget.tsx | 83 +++--- .../ui/componets/DroppedFloatingWidgets.tsx | 254 ++++++++++-------- app/src/components/ui/componets/Panel.tsx | 2 +- .../ui/componets/RealTimeVisulization.tsx | 94 ++++--- .../functions/convertAutoToNumeric.ts | 41 +++ .../componets/functions/determinePosition.ts | 64 +++++ .../functions/getActiveProperties.ts | 17 ++ .../ui/componets/zoneCameraTarget.tsx | 33 +-- app/src/components/ui/list/DropDownList.tsx | 13 +- app/src/components/ui/list/List.tsx | 64 +++-- .../realTimeVis/charts/PieGraphComponent.tsx | 2 +- .../realTimeVis/floating/FleetEfficiency.tsx | 2 - .../ui/realTimeVis/floating/SimpleCard.tsx | 1 + app/src/modules/scene/scene.tsx | 6 +- .../zoneData/addFloatingWidgets.ts | 33 +++ .../zoneData/addWidgets.ts | 2 +- .../zoneData/deleteWidgetApi.ts | 30 +++ .../zoneData/duplicateWidget.ts | 30 +++ .../zoneData/getFloatingData.ts | 26 ++ .../zoneData/getSelect2dZoneData.ts | 2 +- .../zoneData/getZoneData.ts | 2 +- .../realTimeVisulization/zoneData/getZones.ts | 3 + .../realTimeVisulization/zoneData/panel.ts | 2 +- .../zoneData/zoneCameraUpdation.ts | 29 ++ app/src/store/store.ts | 13 +- app/src/store/useDroppedObjectsStore.ts | 42 ++- 34 files changed, 792 insertions(+), 327 deletions(-) create mode 100644 app/src/components/ui/componets/functions/convertAutoToNumeric.ts create mode 100644 app/src/components/ui/componets/functions/determinePosition.ts create mode 100644 app/src/components/ui/componets/functions/getActiveProperties.ts create mode 100644 app/src/services/realTimeVisulization/zoneData/addFloatingWidgets.ts create mode 100644 app/src/services/realTimeVisulization/zoneData/deleteWidgetApi.ts create mode 100644 app/src/services/realTimeVisulization/zoneData/duplicateWidget.ts create mode 100644 app/src/services/realTimeVisulization/zoneData/getFloatingData.ts create mode 100644 app/src/services/realTimeVisulization/zoneData/zoneCameraUpdation.ts diff --git a/app/package-lock.json b/app/package-lock.json index c8210e0..736405a 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -2017,7 +2017,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" }, @@ -2029,7 +2029,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" @@ -4128,6 +4128,26 @@ "url": "https://github.com/sponsors/gregberge" } }, + "node_modules/@testing-library/dom": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.0.tgz", + "integrity": "sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^5.0.1", + "aria-query": "5.3.0", + "chalk": "^4.1.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.5.0", + "pretty-format": "^27.0.2" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/@testing-library/jest-dom": { "version": "5.17.0", "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.17.0.tgz", @@ -4239,25 +4259,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", @@ -8864,7 +8884,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", @@ -9729,7 +9749,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" } @@ -15073,7 +15093,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", @@ -20516,7 +20536,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", @@ -20559,7 +20579,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" }, @@ -20571,7 +20591,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", @@ -21058,7 +21078,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", @@ -22117,7 +22137,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/src/components/layout/sidebarLeft/visualization/widgets/Widgets.tsx b/app/src/components/layout/sidebarLeft/visualization/widgets/Widgets.tsx index 15bc313..d4a64ae 100644 --- a/app/src/components/layout/sidebarLeft/visualization/widgets/Widgets.tsx +++ b/app/src/components/layout/sidebarLeft/visualization/widgets/Widgets.tsx @@ -3,24 +3,26 @@ import ToggleHeader from "../../../../ui/inputs/ToggleHeader"; import Widgets2D from "./Widgets2D"; import Widgets3D from "./Widgets3D"; import WidgetsFloating from "./WidgetsFloating"; +import { useWidgetSubOption } from "../../../../../store/store"; const Widgets = () => { const [activeOption, setActiveOption] = useState("2D"); + const { widgetSubOption, setWidgetSubOption } = useWidgetSubOption(); const handleToggleClick = (option: string) => { - setActiveOption(option); + setWidgetSubOption(option); }; return (
- {activeOption === "2D" && } - {activeOption === "3D" && } - {activeOption === "Floating" && } + {widgetSubOption === "2D" && } + {widgetSubOption === "3D" && } + {widgetSubOption === "Floating" && }
); }; diff --git a/app/src/components/layout/sidebarLeft/visualization/widgets/Widgets3D.tsx b/app/src/components/layout/sidebarLeft/visualization/widgets/Widgets3D.tsx index 849c051..6872497 100644 --- a/app/src/components/layout/sidebarLeft/visualization/widgets/Widgets3D.tsx +++ b/app/src/components/layout/sidebarLeft/visualization/widgets/Widgets3D.tsx @@ -24,9 +24,8 @@ const Widgets3D = () => { let crt = e.target if (crt instanceof HTMLElement) { const widget = crt.cloneNode(true) as HTMLElement; - console.log('widget: ', widget); - e.dataTransfer.setDragImage(widget,0,0) - e.dataTransfer.effectAllowed="move" + e.dataTransfer.setDragImage(widget, 0, 0) + e.dataTransfer.effectAllowed = "move" } }} onPointerDown={() => { @@ -41,7 +40,7 @@ const Widgets3D = () => { className="widget-image" src={widget.img} alt={widget.name} - // draggable={false} + // draggable={false} />
))} diff --git a/app/src/components/layout/sidebarRight/properties/ZoneProperties.tsx b/app/src/components/layout/sidebarRight/properties/ZoneProperties.tsx index c707f14..9234cc5 100644 --- a/app/src/components/layout/sidebarRight/properties/ZoneProperties.tsx +++ b/app/src/components/layout/sidebarRight/properties/ZoneProperties.tsx @@ -3,6 +3,7 @@ import RenameInput from "../../../ui/inputs/RenameInput"; import Vector3Input from "../customInput/Vector3Input"; import { useSelectedZoneStore } from "../../../../store/useZoneStore"; import { useEditPosition, usezonePosition, usezoneTarget } from "../../../../store/store"; +import { zoneCameraUpdate } from "../../../../services/realTimeVisulization/zoneData/zoneCameraUpdation"; const ZoneProperties: React.FC = () => { const { Edit, setEdit } = useEditPosition(); @@ -15,12 +16,25 @@ const ZoneProperties: React.FC = () => { setZoneTarget(selectedZone.zoneViewPortTarget) }, [selectedZone?.zoneViewPortPosition, selectedZone?.zoneViewPortTarget]) - function handleSetView() { - console.log("setApi"); - - console.log('zoneTarget: ', zoneTarget); - console.log('zonePosition: ', zonePosition); - setEdit(false); + async function handleSetView() { + 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); + } } function handleEditView() { @@ -36,7 +50,7 @@ const ZoneProperties: React.FC = () => { } useEffect(() => { - console.log("Updated selectedZone: ", selectedZone); + }, [selectedZone]); return ( diff --git a/app/src/components/ui/ModuleToggle.tsx b/app/src/components/ui/ModuleToggle.tsx index e364f52..3f028c2 100644 --- a/app/src/components/ui/ModuleToggle.tsx +++ b/app/src/components/ui/ModuleToggle.tsx @@ -1,4 +1,4 @@ -import React from "react"; +import React, { useEffect } from "react"; import useModuleStore from "../../store/useModuleStore"; import { BuilderIcon, @@ -7,11 +7,13 @@ import { VisualizationIcon, } from "../icons/ExportModuleIcons"; import useToggleStore from "../../store/useUIToggleStore"; +import { useSelectedZoneStore } from "../../store/useZoneStore"; const ModuleToggle: React.FC = () => { const { activeModule, setActiveModule } = useModuleStore(); const { setToggleUI } = useToggleStore(); + return (
{
Simulation
{ setActiveModule("visualization"); setToggleUI(true); diff --git a/app/src/components/ui/componets/AddButtons.tsx b/app/src/components/ui/componets/AddButtons.tsx index 11a1d80..4f3f9b7 100644 --- a/app/src/components/ui/componets/AddButtons.tsx +++ b/app/src/components/ui/componets/AddButtons.tsx @@ -119,7 +119,7 @@ const AddButtons: React.FC = ({ }; // Delete the selectedZone state - console.log("updatedZone: ", updatedZone); + setSelectedZone(updatedZone); } else { const updatePanelData = async () => { @@ -141,13 +141,13 @@ const AddButtons: React.FC = ({ // API call const response = await panelData(organization, selectedZone.zoneId, newActiveSides); - console.log("response: ", response); + // Update state - console.log("updatedZone: ", updatedZone); + setSelectedZone(updatedZone); } catch (error) { - console.error("Error updating panel data:", error); + } }; diff --git a/app/src/components/ui/componets/DisplayZone.tsx b/app/src/components/ui/componets/DisplayZone.tsx index dafddba..d0dce1f 100644 --- a/app/src/components/ui/componets/DisplayZone.tsx +++ b/app/src/components/ui/componets/DisplayZone.tsx @@ -4,6 +4,7 @@ import { MoveArrowLeft, MoveArrowRight } from "../../icons/SimulationIcons"; import { InfoIcon } from "../../icons/ExportCommonIcons"; import { useDroppedObjectsStore } from "../../../store/useDroppedObjectsStore"; import { getSelect2dZoneData } from "../../../services/realTimeVisulization/zoneData/getSelect2dZoneData"; +import { getFloatingZoneData } from "../../../services/realTimeVisulization/zoneData/getFloatingData"; // Define the type for `Side` type Side = "top" | "bottom" | "left" | "right"; @@ -147,23 +148,41 @@ const DisplayZone: React.FC = ({ }; async function handleSelect2dZoneData(zoneId: string, zoneName: string) { - const email = localStorage.getItem("email") || ""; - const organization = email?.split("@")[1]?.split(".")[0] - let response = await getSelect2dZoneData(zoneId, organization) - setSelectedZone({ - zoneName, - activeSides: response.activeSides, - panelOrder: response.panelOrder, - lockedPanels: response.lockedPanels, - widgets: response.widgets, - zoneId: zoneId, - zoneViewPortTarget: - response.viewPortCenter, - zoneViewPortPosition: - response.viewPortposition, - }); + 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); + let res = await getFloatingZoneData(zoneId, organization); + // Set the selected zone in the store + useDroppedObjectsStore.getState().setZone(zoneName, zoneId); + if (Array.isArray(res)) { + res.forEach((val) => { + useDroppedObjectsStore.getState().addObject(zoneName, val); + }); + } + // Update selected zone state + setSelectedZone({ + zoneName, + activeSides: response.activeSides || [], + panelOrder: response.panelOrder || [], + lockedPanels: response.lockedPanels || [], + widgets: response.widgets || [], + zoneId: zoneId, + zoneViewPortTarget: response.viewPortCenter || {}, + zoneViewPortPosition: response.viewPortposition || {}, + }); + } catch (error) { + console.log('error: ', error); + + } } + return (
= ({ className={`zone ${selectedZone.zoneName === zoneName ? "active" : "" }`} onClick={() => { - useDroppedObjectsStore.getState().setZone(zoneName, zonesData[zoneName]?.zoneId); handleSelect2dZoneData(zonesData[zoneName]?.zoneId, zoneName) }} > diff --git a/app/src/components/ui/componets/DraggableWidget.tsx b/app/src/components/ui/componets/DraggableWidget.tsx index 5666af8..83ba518 100644 --- a/app/src/components/ui/componets/DraggableWidget.tsx +++ b/app/src/components/ui/componets/DraggableWidget.tsx @@ -11,6 +11,8 @@ import { KebabIcon, } from "../../icons/ExportCommonIcons"; import { useEffect, useRef, useState } from "react"; +import { duplicateWidgetApi } from "../../../services/realTimeVisulization/zoneData/duplicateWidget"; +import { deleteWidgetApi } from "../../../services/realTimeVisulization/zoneData/deleteWidgetApi"; type Side = "top" | "bottom" | "left" | "right"; @@ -34,9 +36,9 @@ export const DraggableWidget = ({ }: { selectedZone: { zoneName: string; + zoneId: string; activeSides: Side[]; panelOrder: Side[]; - lockedPanels: Side[]; widgets: Widget[]; }; @@ -79,21 +81,28 @@ export const DraggableWidget = ({ const isPanelHidden = hiddenPanels.includes(widget.panel); - const deleteSelectedChart = () => { - console.log('widget.id: ', widget.id); - const updatedWidgets = selectedZone.widgets.filter( - (w: Widget) => w.id !== widget.id - ); - console.log('updatedWidgets: ', updatedWidgets); + const deleteSelectedChart = async () => { + try { + const email = localStorage.getItem("email") || ""; + const organization = email?.split("@")[1]?.split(".")[0]; + const response = await deleteWidgetApi(widget.id, organization); + if (response?.message === "Widget deleted successfully") { + const updatedWidgets = selectedZone.widgets.filter( + (w: Widget) => w.id !== widget.id + ); + setSelectedZone((prevZone: any) => ({ + ...prevZone, + widgets: updatedWidgets, + })); + } + } catch (error) { - setSelectedZone((prevZone: any) => ({ - ...prevZone, - widgets: updatedWidgets, - })); - - setOpenKebabId(null); + } finally { + setOpenKebabId(null); + } }; + const getCurrentWidgetCount = (panel: Side) => selectedZone.widgets.filter((w) => w.panel === panel).length; @@ -121,21 +130,32 @@ export const DraggableWidget = ({ return currentWidgetCount >= panelCapacity; }; - const duplicateWidget = () => { - const duplicatedWidget: Widget = { - ...widget, - id: `${widget.id}-copy-${Date.now()}`, - }; - setSelectedZone((prevZone: any) => ({ - ...prevZone, - widgets: [...prevZone.widgets, duplicatedWidget], - })); + const duplicateWidget = async () => { + try { + const email = localStorage.getItem("email") || ""; + const organization = email?.split("@")[1]?.split(".")[0]; - setOpenKebabId(null); + const duplicatedWidget: Widget = { + ...widget, + id: `${widget.id}-copy-${Date.now()}`, + }; + const response = await duplicateWidgetApi(selectedZone.zoneId, organization, duplicatedWidget); + if (response?.message === "Widget created successfully") { + setSelectedZone((prevZone: any) => ({ + ...prevZone, + widgets: [...prevZone.widgets, duplicatedWidget], + })); + } + } catch (error) { + + } finally { + setOpenKebabId(null); + } }; + const handleKebabClick = (event: React.MouseEvent) => { event.stopPropagation(); if (openKebabId === widget.id) { @@ -176,7 +196,6 @@ export const DraggableWidget = ({ }; const handleDrop = (event: React.DragEvent) => { - event.preventDefault(); const fromIndex = parseInt(event.dataTransfer.getData("text/plain"), 10); // Get the dragged widget's index const toIndex = index; // The index of the widget where the drop occurred diff --git a/app/src/components/ui/componets/Dropped3dWidget.tsx b/app/src/components/ui/componets/Dropped3dWidget.tsx index e0e2a7d..10dd343 100644 --- a/app/src/components/ui/componets/Dropped3dWidget.tsx +++ b/app/src/components/ui/componets/Dropped3dWidget.tsx @@ -1,7 +1,7 @@ import { useThree } from "@react-three/fiber"; import React, { useState, useEffect } from "react"; -import { useAsset3dWidget } from "../../../store/store"; +import { useAsset3dWidget, useWidgetSubOption } from "../../../store/store"; import useModuleStore from "../../../store/useModuleStore"; import { ThreeState } from "../../../types/world/worldTypes"; import * as THREE from "three"; @@ -9,71 +9,80 @@ import Throughput from "../../layout/3D-cards/cards/Throughput"; 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"; export default function Dropped3dWidgets() { - const { widgetSelect } = useAsset3dWidget(); + const { widgetSelect, setWidgetSelect } = 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 + >>({}); - // 🔥 Store multiple instances per widget type - const [widgetPositions, setWidgetPositions] = useState>({}); useEffect(() => { - if (activeModule !== "visualization") return; + 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 (!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(Assets); - - if (intersects.length > 0) { - const { x, y, z } = intersects[0].point; - - - // ✅ Allow multiple instances by storing positions in an array - setWidgetPositions((prev) => ({ - ...prev, - [widgetSelect]: [...(prev[widgetSelect] || []), [x, y, z]], - })); + setZoneWidgets((prev) => ({ + ...prev, + [selectedZone.zoneId]: { + ...(prev[selectedZone.zoneId] || {}), + [widgetSelect]: [...(prev[selectedZone.zoneId]?.[widgetSelect] || []), [x, y, z]], + }, + })); + } } }; canvasElement.addEventListener("drop", onDrop); - return () => { - canvasElement.removeEventListener("drop", onDrop); + canvasElement.removeEventListener("drop", onDrop) + // setWidgetSelect() }; - }, [widgetSelect, activeModule]); + }, [widgetSelect, activeModule, widgetSubOption]); return ( <> - {widgetPositions["ui-Widget 1"]?.map((pos, index) => ( + {zoneWidgets[selectedZone.zoneId]?.["ui-Widget 1"]?.map((pos, index) => ( ))} - {widgetPositions["ui-Widget 2"]?.map((pos, index) => ( + {zoneWidgets[selectedZone.zoneId]?.["ui-Widget 2"]?.map((pos, index) => ( ))} - {widgetPositions["ui-Widget 3"]?.map((pos, index) => ( + {zoneWidgets[selectedZone.zoneId]?.["ui-Widget 3"]?.map((pos, index) => ( ))} - {widgetPositions["ui-Widget 4"]?.map((pos, index) => ( + {zoneWidgets[selectedZone.zoneId]?.["ui-Widget 4"]?.map((pos, index) => ( ))} ); } + diff --git a/app/src/components/ui/componets/DroppedFloatingWidgets.tsx b/app/src/components/ui/componets/DroppedFloatingWidgets.tsx index ffddc23..bf19bb9 100644 --- a/app/src/components/ui/componets/DroppedFloatingWidgets.tsx +++ b/app/src/components/ui/componets/DroppedFloatingWidgets.tsx @@ -1,59 +1,79 @@ - - - import { WalletIcon } from "../../icons/3dChartIcons"; import { useEffect, useRef, useState } from "react"; import { Line } from "react-chartjs-2"; -import { useDroppedObjectsStore, Zones } from "../../../store/useDroppedObjectsStore"; +import { + useDroppedObjectsStore, + Zones, +} from "../../../store/useDroppedObjectsStore"; +import useModuleStore from "../../../store/useModuleStore"; +import { determinePosition } from "./functions/determinePosition"; +import { getActiveProperties } from "./functions/getActiveProperties"; +import { addingFloatingWidgets } from "../../../services/realTimeVisulization/zoneData/addFloatingWidgets"; const DroppedObjects: React.FC = () => { const zones = useDroppedObjectsStore((state) => state.zones); - const updateObjectPosition = useDroppedObjectsStore((state) => state.updateObjectPosition); - const [draggingIndex, setDraggingIndex] = useState<{ zone: string; index: number } | null>(null); + + const updateObjectPosition = useDroppedObjectsStore( + (state) => state.updateObjectPosition + ); + const [draggingIndex, setDraggingIndex] = useState<{ + zone: string; + index: number; + } | null>(null); const [offset, setOffset] = useState<[number, number] | null>(null); const positionRef = useRef<[number, number] | null>(null); const animationRef = useRef(null); + const { activeModule } = useModuleStore(); - // useEffect(() => { - // const initialZones: Record = { - // "Zone 1": { - // zoneName: "Zone 1", - // zoneId: "2e996073-546c-470c-8323-55bd3700c6aa", - // objects: [ - // { - // header: "Today’s Money", - // value: 53000, // ✅ Converted to number - // per: "+55%", - // className: "floating total-card", - // position: [146, 214], // ✅ No need for 'as' here - // }, - // { - // header: "New Clients", - // value: 250, // ✅ Converted to number - // per: "+12%", - // className: "floating total-card", - // position: [344, 295], - // }, - // ], - // }, - // }; - - // useDroppedObjectsStore.setState({ zones: initialZones }); - // }, []); - + // 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 + // Handle pointer down event function handlePointerDown(event: React.PointerEvent, index: number) { const obj = zone.objects[index]; - const offsetX = event.clientX - obj.position[1]; - const offsetY = event.clientY - obj.position[0]; + 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; + + // Determine which properties are active for this object + const [activeProp1, activeProp2] = getActiveProperties(obj.position); + + // Calculate the offset based on the active properties + 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); + } + + 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); + } setDraggingIndex({ zone: zoneName, index }); setOffset([offsetY, offsetX]); } + // Handle pointer move event function handlePointerMove(event: React.PointerEvent) { if (!draggingIndex || !offset) return; @@ -61,34 +81,92 @@ const DroppedObjects: React.FC = () => { if (!container) return; const rect = container.getBoundingClientRect(); + const relativeX = event.clientX - rect.left; + const relativeY = event.clientY - rect.top; - let newX = event.clientX - offset[1]; - let newY = event.clientY - offset[0]; + // Determine which properties are active for the dragged object + const obj = zone.objects[draggingIndex.index]; + const [activeProp1, activeProp2] = getActiveProperties(obj.position); + // Calculate the new position based on the active properties + let newX = 0; + let newY = 0; + + if (activeProp2 === "left") { + newX = relativeX - offset[1]; + } else if (activeProp2 === "right") { + newX = rect.width - (relativeX + offset[1]); + } + + if (activeProp1 === "top") { + newY = relativeY - offset[0]; + } else if (activeProp1 === "bottom") { + newY = rect.height - (relativeY + offset[0]); + } + + // Ensure the object stays within the canvas 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]; + // Update the object's position using requestAnimationFrame for smoother animations if (!animationRef.current) { animationRef.current = requestAnimationFrame(() => { if (positionRef.current) { - updateObjectPosition(zoneName, draggingIndex.index, positionRef.current); + updateObjectPosition(zoneName, draggingIndex.index, { + ...obj.position, + [activeProp1]: positionRef.current[0], + [activeProp2]: positionRef.current[1], + }); } animationRef.current = null; }); } } - function handlePointerUp() { - setDraggingIndex(null); - setOffset(null); - if (animationRef.current) { - cancelAnimationFrame(animationRef.current); - animationRef.current = null; + // Handle pointer up event + async function handlePointerUp(event: React.MouseEvent) { + 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"); + + 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); + + // Validate the dragging index and get the object + if (!zone.objects[draggingIndex.index]) { + throw new Error("Dragged object not found in the zone"); + } + const obj = { ...zone.objects[draggingIndex.index], position: newPosition }; + let response = await addingFloatingWidgets(zone.zoneId, organization, obj); + if (response.message === "Widget updated successfully") { + updateObjectPosition(zoneName, draggingIndex.index, newPosition); + } + + // Reset states + setDraggingIndex(null); + setOffset(null); + + if (animationRef.current) { + cancelAnimationFrame(animationRef.current); + animationRef.current = null; + } + } catch (error) { + } } + return (
{zone.objects.map((obj, index) => ( @@ -96,14 +174,29 @@ const DroppedObjects: React.FC = () => { key={`${zoneName}-${index}`} className={obj.className} style={{ - top: obj.position[0] + "px", - left: obj.position[1] + "px", - transition: draggingIndex?.index === index ? "none" : "transform 0.1s ease-out", + position: "absolute", + top: + typeof obj.position.top !== "string" + ? `${obj.position.top}px` + : "auto", + left: + typeof obj.position.left !== "string" + ? `${obj.position.left}px` + : "auto", + right: + typeof obj.position.right !== "string" + ? `${obj.position.right}px` + : "auto", + bottom: + typeof obj.position.bottom !== "string" + ? `${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}
@@ -114,9 +207,9 @@ const DroppedObjects: React.FC = () => {
-
+ ) : obj.className === "warehouseThroughput floating" ? ( -
+ <>

Warehouse Throughput

@@ -126,9 +219,9 @@ const DroppedObjects: React.FC = () => {

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

Fleet Efficiency

@@ -148,7 +241,7 @@ const DroppedObjects: React.FC = () => {
100%
-
+ ) : null}
))} @@ -156,59 +249,4 @@ const DroppedObjects: React.FC = () => { ); }; - - - - export default DroppedObjects; - - - - - - - - - - - - - - - - -// import { Html } from "@react-three/drei"; -// import { useDroppedObjectsStore } from "../../../store/store"; -// import { CartIcon, DocumentIcon, GlobeIcon, WalletIcon } from "../../icons/3dChartIcons"; -// import SimpleCard from "../realTimeVis/floating/SimpleCard"; - -// const ICON_MAP: Record>> = { -// WalletIcon, -// GlobeIcon, -// DocumentIcon, -// CartIcon -// }; -// const DroppedObjects: React.FC = () => { -// const objects = useDroppedObjectsStore((state) => state.objects); // Get objects from Zustand store -// - -// return ( -// <> -// {objects.map((obj, index) => { -// const IconComponent = obj.Icon || WalletIcon; // Use obj.Icon directly if it exists -// return ( -// -// ); -// })} -// -// ); -// }; - -// export default DroppedObjects; diff --git a/app/src/components/ui/componets/Panel.tsx b/app/src/components/ui/componets/Panel.tsx index 1d4cc44..6500119 100644 --- a/app/src/components/ui/componets/Panel.tsx +++ b/app/src/components/ui/componets/Panel.tsx @@ -149,7 +149,7 @@ const Panel: React.FC = ({ }; try { let response = await addingWidgets(selectedZone.zoneId, organization, newWidget); - console.log("response: ", response); + if (response.message === "Widget created successfully") { setSelectedZone((prev) => ({ ...prev, diff --git a/app/src/components/ui/componets/RealTimeVisulization.tsx b/app/src/components/ui/componets/RealTimeVisulization.tsx index 27044b1..d378345 100644 --- a/app/src/components/ui/componets/RealTimeVisulization.tsx +++ b/app/src/components/ui/componets/RealTimeVisulization.tsx @@ -9,9 +9,11 @@ import useModuleStore from "../../../store/useModuleStore"; import DroppedObjects from "./DroppedFloatingWidgets"; import { useDroppedObjectsStore } from "../../../store/useDroppedObjectsStore"; -import { useAsset3dWidget, useZones } from "../../../store/store"; -import { getZoneData } from "../../../services/realTimeVisulization/zoneData/getZones"; +import { useAsset3dWidget, useWidgetSubOption, useZones } from "../../../store/store"; import { getZone2dData } from "../../../services/realTimeVisulization/zoneData/getZoneData"; +import { generateUniqueId } from "../../../functions/generateUniqueId"; +import { determinePosition } from "./functions/determinePosition"; +import { addingFloatingWidgets } from "../../../services/realTimeVisulization/zoneData/addFloatingWidgets"; type Side = "top" | "bottom" | "left" | "right"; @@ -48,6 +50,8 @@ const RealTimeVisulization: React.FC = () => { const { zones } = useZones() const [floatingWidgets, setFloatingWidgets] = useState>({}); const { widgetSelect, setWidgetSelect } = useAsset3dWidget(); + const { widgetSubOption, setWidgetSubOption } = useWidgetSubOption() + useEffect(() => { async function GetZoneData() { const email = localStorage.getItem("email") || ""; @@ -72,12 +76,12 @@ const RealTimeVisulization: React.FC = () => { setZonesData(formattedData); } catch (error) { console.log('error: ', error); + } } GetZoneData(); - }, []); // Removed `zones` from dependencies - + }, [activeModule]); // Removed `zones` from dependencies useEffect(() => { setZonesData((prev) => { @@ -97,48 +101,68 @@ const RealTimeVisulization: React.FC = () => { }; }); }, [selectedZone]); - + useEffect(() => { }, [floatingWidgets]) - const handleDrop = (event: React.DragEvent) => { - event.preventDefault(); - const data = event.dataTransfer.getData("text/plain"); - if (widgetSelect !== "") return; - if (!data || selectedZone.zoneName === "") return; + const handleDrop = async (event: React.DragEvent) => { + try { + event.preventDefault(); + const email = localStorage.getItem("email") || ""; + const organization = email?.split("@")[1]?.split(".")[0]; - const droppedData = JSON.parse(data); - const canvasElement = document.getElementById("real-time-vis-canvas"); - if (!canvasElement) return; + const data = event.dataTransfer.getData("text/plain"); + // if (widgetSelect !== "") return; + if (widgetSubOption === "3D") return + if (!data || selectedZone.zoneName === "") return; - const canvasRect = canvasElement.getBoundingClientRect(); - const relativeX = event.clientX - canvasRect.left; - const relativeY = event.clientY - canvasRect.top; + const droppedData = JSON.parse(data); + const canvasElement = document.getElementById("real-time-vis-canvas"); + if (!canvasElement) throw new Error("Canvas element not found"); + + const canvasRect = canvasElement.getBoundingClientRect(); + const relativeX = event.clientX - canvasRect.left; + const relativeY = event.clientY - canvasRect.top; + + const newObject = { + ...droppedData, + id: generateUniqueId(), + position: determinePosition(canvasRect, relativeX, relativeY), + }; + + let response = await addingFloatingWidgets(selectedZone.zoneId, organization, newObject); + + + // Only set zone if it’s not already in the store (prevents overwriting objects) + const existingZone = useDroppedObjectsStore.getState().zones[selectedZone.zoneName]; + if (!existingZone) { + useDroppedObjectsStore.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.getState().addObject(selectedZone.zoneName, newObject); + } + + // Update floating widgets state + setFloatingWidgets((prevWidgets) => ({ + ...prevWidgets, + [selectedZone.zoneName]: { + ...prevWidgets[selectedZone.zoneName], + zoneName: selectedZone.zoneName, + zoneId: selectedZone.zoneId, + objects: [...(prevWidgets[selectedZone.zoneName]?.objects || []), newObject], + }, + })); + } catch (error) { - const newObject = { - ...droppedData, - position: [relativeY, relativeX], // Y first because of top/left style - }; - // Only set zone if it’s not already in the store (prevents overwriting objects) - const existingZone = useDroppedObjectsStore.getState().zones[selectedZone.zoneName]; - if (!existingZone) { - useDroppedObjectsStore.getState().setZone(selectedZone.zoneName, selectedZone.zoneId); } - // Add the dropped object to the zone - useDroppedObjectsStore.getState().addObject(selectedZone.zoneName, newObject); - setFloatingWidgets((prevWidgets) => ({ - ...prevWidgets, - [selectedZone.zoneName]: { - ...prevWidgets[selectedZone.zoneName], - zoneName: selectedZone.zoneName, - zoneId: selectedZone.zoneId, - objects: [...(prevWidgets[selectedZone.zoneName]?.objects || []), newObject], - }, - })); }; + return (
{ + const { width, height } = canvasRect; + + // Determine which properties are active + const [activeProp1, activeProp2] = getActiveProperties(position); + + let top = typeof position.top !== "string" ? position.top : 0; + let left = typeof position.left !== "string" ? position.left : 0; + let right = typeof position.right !== "string" ? position.right : 0; + let bottom = typeof position.bottom !== "string" ? position.bottom : 0; + + // Calculate missing properties based on active properties + if (activeProp1 === "top") { + bottom = height - top; + } else if (activeProp1 === "bottom") { + top = height - bottom; + } + + if (activeProp2 === "left") { + right = width - left; + } else if (activeProp2 === "right") { + left = width - right; + } + + return { + top, + left, + right, + bottom, + }; +}; diff --git a/app/src/components/ui/componets/functions/determinePosition.ts b/app/src/components/ui/componets/functions/determinePosition.ts new file mode 100644 index 0000000..0fcd727 --- /dev/null +++ b/app/src/components/ui/componets/functions/determinePosition.ts @@ -0,0 +1,64 @@ +export function determinePosition( + canvasRect: DOMRect, + relativeX: number, + relativeY: number +): { + top: number | "auto"; + left: number | "auto"; + 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"; + right: number | "auto"; + bottom: number | "auto"; + }; + + if (relativeY < centerY) { + // Top half + if (relativeX < centerX) { + // Left side + position = { + top: relativeY, + left: relativeX, + right: "auto", + bottom: "auto", + }; + } else { + // Right side + position = { + top: relativeY, + right: canvasRect.width - relativeX, + left: "auto", + bottom: "auto", + }; + } + } else { + // Bottom half + if (relativeX < centerX) { + // Left side + position = { + bottom: canvasRect.height - relativeY, + left: relativeX, + right: "auto", + top: "auto", + }; + } else { + // Right side + position = { + bottom: canvasRect.height - relativeY, + right: canvasRect.width - relativeX, + left: "auto", + top: "auto", + }; + } + } + + return position; +} diff --git a/app/src/components/ui/componets/functions/getActiveProperties.ts b/app/src/components/ui/componets/functions/getActiveProperties.ts new file mode 100644 index 0000000..2cb0b1b --- /dev/null +++ b/app/src/components/ui/componets/functions/getActiveProperties.ts @@ -0,0 +1,17 @@ +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"]; + } + + return activeProps; +}; diff --git a/app/src/components/ui/componets/zoneCameraTarget.tsx b/app/src/components/ui/componets/zoneCameraTarget.tsx index 57804b9..f022b32 100644 --- a/app/src/components/ui/componets/zoneCameraTarget.tsx +++ b/app/src/components/ui/componets/zoneCameraTarget.tsx @@ -14,6 +14,7 @@ export default function ZoneCentreTarget() { const { Edit, setEdit } = useEditPosition(); + useEffect(() => { if ( selectedZone.zoneViewPortTarget && @@ -39,22 +40,22 @@ export default function ZoneCentreTarget() { if (centrePoint.length > 0) { - let camPosition = new THREE.Vector3(...selectedZone.zoneViewPortPosition); - let CamTarget = new THREE.Vector3(...selectedZone.zoneViewPortTarget); + // let camPosition = new THREE.Vector3(...selectedZone.zoneViewPortPosition); + // let CamTarget = new THREE.Vector3(...selectedZone.zoneViewPortTarget); - const direction = new THREE.Vector3().subVectors(CamTarget, camPosition).normalize(); + // const direction = new THREE.Vector3().subVectors(CamTarget, camPosition).normalize(); - const worldUp = new THREE.Vector3(0, 0, 1); - const right = new THREE.Vector3().crossVectors(worldUp, direction).normalize(); - const up = new THREE.Vector3().crossVectors(direction, right).normalize(); - const offsetPosition = up.clone().multiplyScalar(20); - camPosition.add(offsetPosition); + // const worldUp = new THREE.Vector3(0, 0, 1); + // const right = new THREE.Vector3().crossVectors(worldUp, direction).normalize(); + // const up = new THREE.Vector3().crossVectors(direction, right).normalize(); + // const offsetPosition = up.clone().multiplyScalar(20); + // camPosition.add(offsetPosition); const setCam = async () => { controls.setLookAt(centrePoint[0], 100, centrePoint[2], ...centrePoint, true); setTimeout(() => { controls?.setLookAt( - ...camPosition.toArray(), + ...selectedZone.zoneViewPortPosition, selectedZone.zoneViewPortTarget[0], selectedZone.zoneViewPortTarget[1], selectedZone.zoneViewPortTarget[2], @@ -65,21 +66,9 @@ export default function ZoneCentreTarget() { setCam(); } else { - let camPosition = new THREE.Vector3(...selectedZone.zoneViewPortPosition); - let CamTarget = new THREE.Vector3(...selectedZone.zoneViewPortTarget); - - const direction = new THREE.Vector3().subVectors(CamTarget, camPosition).normalize(); - - const worldUp = new THREE.Vector3(0, 0, 1); - const right = new THREE.Vector3().crossVectors(worldUp, direction).normalize(); - const up = new THREE.Vector3().crossVectors(direction, right).normalize(); - - const offsetPosition = up.clone().multiplyScalar(20); - - camPosition.add(offsetPosition); const setCam = async () => { controls?.setLookAt( - ...camPosition.toArray(), + ...selectedZone.zoneViewPortPosition, selectedZone.zoneViewPortTarget[0], selectedZone.zoneViewPortTarget[1], selectedZone.zoneViewPortTarget[2], diff --git a/app/src/components/ui/list/DropDownList.tsx b/app/src/components/ui/list/DropDownList.tsx index 4ff51ca..e77f35b 100644 --- a/app/src/components/ui/list/DropDownList.tsx +++ b/app/src/components/ui/list/DropDownList.tsx @@ -32,7 +32,7 @@ const DropDownList: React.FC = ({ listType = "default", remove, }) => { - + const [isOpen, setIsOpen] = useState(defaultOpen); const { zones, setZones } = useZones() @@ -43,6 +43,17 @@ const DropDownList: React.FC = ({ const { selectedZone, setSelectedZone } = useSelectedZoneStore(); useEffect(() => { + // console.log(zones); + // setZoneDataList([ + // { id: "2e996073-546c-470c-8323-55bd3700c6aa", name: "zone1" }, + // { id: "3f473bf0-197c-471c-a71f-943fc9ca2b47", name: "zone2" }, + // { id: "905e8fb6-9e18-469b-9474-e0478fb9601b", name: "zone3" }, + // { id: "9d9efcbe-8e96-47eb-bfad-128a9e4c532e", name: "zone4" }, + // { id: "884f3d29-eb5a-49a5-abe9-d11971c08e85", name: "zone5" }, + // { id: "70fa55cd-b5c9-4f80-a8c4-6319af3bfb4e", name: "zone6" }, + // ]) + + const value = (zones || []).map((val: { zoneId: string; zoneName: string }) => ({ id: val.zoneId, name: val.zoneName diff --git a/app/src/components/ui/list/List.tsx b/app/src/components/ui/list/List.tsx index 7a7da8d..0b2e63b 100644 --- a/app/src/components/ui/list/List.tsx +++ b/app/src/components/ui/list/List.tsx @@ -1,9 +1,9 @@ -import React from "react"; +import React, { useEffect } from "react"; import RenameInput from "../inputs/RenameInput"; import { EyeIcon, LockIcon, RmoveIcon } from "../../icons/ExportCommonIcons"; import { useSelectedZoneStore } from "../../../store/useZoneStore"; import { getZoneData } from "../../../services/realTimeVisulization/zoneData/getZones"; -import { useSubModuleStore } from "../../../store/useModuleStore"; +import useModuleStore, { useSubModuleStore } from "../../../store/useModuleStore"; interface ListProps { items?: { id: string; name: string }[]; // Optional array of items to render @@ -12,27 +12,55 @@ interface ListProps { } const List: React.FC = ({ items = [], remove }) => { - const { setSelectedZone } = useSelectedZoneStore(); + const { activeModule, setActiveModule } = useModuleStore(); + const { selectedZone, setSelectedZone } = useSelectedZoneStore(); const { setSubModule } = useSubModuleStore(); + useEffect(() => { + useSelectedZoneStore.getState().setSelectedZone({ + zoneName: "", + activeSides: [], + panelOrder: [], + lockedPanels: [], + zoneId: "", + zoneViewPortTarget: [], + zoneViewPortPosition: [], + widgets: [], + }); + }, [activeModule]); + async function handleSelectZone(id: string) { - setSubModule("zoneProperties") - const email = localStorage.getItem('email') - const organization = (email!.split("@")[1]).split(".")[0]; - let response = await getZoneData(id, organization) - setSelectedZone({ - zoneName: response?.zoneName, - activeSides: response?.activeSides || [], - panelOrder: response?.panelOrder || [], - lockedPanels: response?.lockedPanels || [], - widgets: response?.widgets || [], - zoneId: response?.zoneId, - zoneViewPortTarget: response?.viewPortCenter || [], - zoneViewPortPosition: - response?.viewPortposition || [], - }); + try { + // Avoid re-fetching if the same zone is already selected + if (selectedZone?.zoneId === id) { + console.log("Zone is already selected:", selectedZone.zoneName); + return; + } + setSubModule("zoneProperties"); + + const email = localStorage.getItem("email"); + const organization = email?.split("@")[1]?.split(".")[0] || ""; + + let response = await getZoneData(id, organization); + + setSelectedZone({ + zoneName: response?.zoneName, + activeSides: response?.activeSides || [], + panelOrder: response?.panelOrder || [], + lockedPanels: response?.lockedPanels || [], + widgets: response?.widgets || [], + zoneId: response?.zoneId, + zoneViewPortTarget: response?.viewPortCenter || [], + zoneViewPortPosition: response?.viewPortposition || [], + }); + + console.log("Zone selected:", response?.zoneName); + } catch (error) { + console.error("Error selecting zone:", error); + } } + return ( <> {items.length > 0 ? ( diff --git a/app/src/components/ui/realTimeVis/charts/PieGraphComponent.tsx b/app/src/components/ui/realTimeVis/charts/PieGraphComponent.tsx index 0066ec3..73d24c7 100644 --- a/app/src/components/ui/realTimeVis/charts/PieGraphComponent.tsx +++ b/app/src/components/ui/realTimeVis/charts/PieGraphComponent.tsx @@ -53,7 +53,7 @@ const PieChartComponent = ({ .getPropertyValue("--accent-color") .trim(); - console.log("accentColor: ", accentColor); + const options = useMemo( () => ({ responsive: true, diff --git a/app/src/components/ui/realTimeVis/floating/FleetEfficiency.tsx b/app/src/components/ui/realTimeVis/floating/FleetEfficiency.tsx index 03c00e8..07e5f19 100644 --- a/app/src/components/ui/realTimeVis/floating/FleetEfficiency.tsx +++ b/app/src/components/ui/realTimeVis/floating/FleetEfficiency.tsx @@ -14,8 +14,6 @@ const FleetEfficiency = () => { per: progress, }); - - console.log("Dragged Data:", cardData); event.dataTransfer.setData("text/plain", cardData); }; diff --git a/app/src/components/ui/realTimeVis/floating/SimpleCard.tsx b/app/src/components/ui/realTimeVis/floating/SimpleCard.tsx index ee996bd..0c36977 100644 --- a/app/src/components/ui/realTimeVis/floating/SimpleCard.tsx +++ b/app/src/components/ui/realTimeVis/floating/SimpleCard.tsx @@ -23,6 +23,7 @@ const SimpleCard: React.FC = ({ value, per, icon: Icon, + className: event.currentTarget.className, position: [rect.top, rect.left], // ✅ Store position }); diff --git a/app/src/modules/scene/scene.tsx b/app/src/modules/scene/scene.tsx index ac74240..74693b4 100644 --- a/app/src/modules/scene/scene.tsx +++ b/app/src/modules/scene/scene.tsx @@ -21,6 +21,7 @@ import DroppedObjects from "../../components/ui/componets/DroppedFloatingWidgets 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() { @@ -31,9 +32,6 @@ export default function Scene() { { name: "right", keys: ["ArrowRight", "d", "D"] }, ], []) - - - return ( - + diff --git a/app/src/services/realTimeVisulization/zoneData/addFloatingWidgets.ts b/app/src/services/realTimeVisulization/zoneData/addFloatingWidgets.ts new file mode 100644 index 0000000..338f26b --- /dev/null +++ b/app/src/services/realTimeVisulization/zoneData/addFloatingWidgets.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 addingFloatingWidgets = async ( + zoneId: string, + organization: string, + widget: {} +) => { + try { + const response = await fetch( + `${url_Backend_dwinzo}/api/v2/floatwidget/save`, + { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ organization, zoneId, widget }), + } + ); + + if (!response.ok) { + throw new Error("Failed to add Floatingwidget 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/addWidgets.ts b/app/src/services/realTimeVisulization/zoneData/addWidgets.ts index 683943e..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`; export const addingWidgets = async ( zoneId: string, organization: string, diff --git a/app/src/services/realTimeVisulization/zoneData/deleteWidgetApi.ts b/app/src/services/realTimeVisulization/zoneData/deleteWidgetApi.ts new file mode 100644 index 0000000..e57e8cb --- /dev/null +++ b/app/src/services/realTimeVisulization/zoneData/deleteWidgetApi.ts @@ -0,0 +1,30 @@ +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 deleteWidgetApi = async ( + widgetID: string, + organization: string +) => { + try { + const response = await fetch(`${url_Backend_dwinzo}/api/v2/delete/widget`, { + method: "PATCH", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ organization, widgetID }), + }); + + if (!response.ok) { + throw new Error("Failed to delete 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/duplicateWidget.ts b/app/src/services/realTimeVisulization/zoneData/duplicateWidget.ts new file mode 100644 index 0000000..f5ec834 --- /dev/null +++ b/app/src/services/realTimeVisulization/zoneData/duplicateWidget.ts @@ -0,0 +1,30 @@ +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 duplicateWidgetApi = async ( + zoneId: string, + organization: string, + widget: {} +) => { + try { + const response = await fetch(`${url_Backend_dwinzo}/api/v2/widget/save`, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ organization, zoneId, widget }), + }); + + if (!response.ok) { + throw new Error("Failed to duplicate 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/getFloatingData.ts b/app/src/services/realTimeVisulization/zoneData/getFloatingData.ts new file mode 100644 index 0000000..80d2b19 --- /dev/null +++ b/app/src/services/realTimeVisulization/zoneData/getFloatingData.ts @@ -0,0 +1,26 @@ +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 +) => { + try { + const response = await fetch( + `${url_Backend_dwinzo}/api/v2/floadData/${ZoneId}/${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/getSelect2dZoneData.ts b/app/src/services/realTimeVisulization/zoneData/getSelect2dZoneData.ts index 71d9c2f..00d4dfe 100644 --- a/app/src/services/realTimeVisulization/zoneData/getSelect2dZoneData.ts +++ b/app/src/services/realTimeVisulization/zoneData/getSelect2dZoneData.ts @@ -1,5 +1,5 @@ let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`; -console.log("url_Backend_dwinzo: ", url_Backend_dwinzo); +// let url_Backend_dwinzo = `http://192.168.0.102:5000`; export const getSelect2dZoneData = async ( ZoneId?: string, diff --git a/app/src/services/realTimeVisulization/zoneData/getZoneData.ts b/app/src/services/realTimeVisulization/zoneData/getZoneData.ts index d2df867..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`; export const getZone2dData = async (organization?: string) => { try { const response = await fetch( diff --git a/app/src/services/realTimeVisulization/zoneData/getZones.ts b/app/src/services/realTimeVisulization/zoneData/getZones.ts index 39031d8..a760959 100644 --- a/app/src/services/realTimeVisulization/zoneData/getZones.ts +++ b/app/src/services/realTimeVisulization/zoneData/getZones.ts @@ -1,6 +1,8 @@ let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`; export const getZoneData = async (zoneId: string, organization: string) => { + console.log("organization: ", organization); + console.log("zoneId: ", zoneId); try { const response = await fetch( `${url_Backend_dwinzo}/api/v2/A_zone/${zoneId}/${organization}`, @@ -12,6 +14,7 @@ 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/panel.ts b/app/src/services/realTimeVisulization/zoneData/panel.ts index 7f89eed..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`; type Side = "top" | "bottom" | "left" | "right"; export const panelData = async ( diff --git a/app/src/services/realTimeVisulization/zoneData/zoneCameraUpdation.ts b/app/src/services/realTimeVisulization/zoneData/zoneCameraUpdation.ts new file mode 100644 index 0000000..d46f6a3 --- /dev/null +++ b/app/src/services/realTimeVisulization/zoneData/zoneCameraUpdation.ts @@ -0,0 +1,29 @@ +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 zoneCameraUpdate = async ( + zonesdata:{},organization:string +) => { + try { + const response = await fetch(`${url_Backend_dwinzo}/api/v2/zone/save`, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ zonesdata, organization }), + }); + + if (!response.ok) { + throw new Error("Failed to update camera position 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"); + } + } +}; \ No newline at end of file diff --git a/app/src/store/store.ts b/app/src/store/store.ts index 8bbcba1..46c1845 100644 --- a/app/src/store/store.ts +++ b/app/src/store/store.ts @@ -29,7 +29,10 @@ export const useSocketStore = create((set: any, get: any) => ({ }, })); -export const useLoadingProgress = create<{ loadingProgress: number; setLoadingProgress: (x: number) => void }>((set) => ({ +export const useLoadingProgress = create<{ + loadingProgress: number; + setLoadingProgress: (x: number) => void; +}>((set) => ({ loadingProgress: 1, setLoadingProgress: (x: number) => set({ loadingProgress: x }), })); @@ -312,7 +315,9 @@ export const useSelectedPath = create((set: any) => ({ interface SimulationPathsStore { simulationPaths: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema)[]; - setSimulationPaths: (paths: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema)[]) => void; + setSimulationPaths: ( + paths: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema)[] + ) => void; } export const useSimulationPaths = create((set) => ({ @@ -352,3 +357,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/store/useDroppedObjectsStore.ts b/app/src/store/useDroppedObjectsStore.ts index e47b12f..dc648a9 100644 --- a/app/src/store/useDroppedObjectsStore.ts +++ b/app/src/store/useDroppedObjectsStore.ts @@ -2,10 +2,17 @@ import { create } from "zustand"; type DroppedObject = { className: string; - position: [number, number]; + id: string; + position: { + top: number | "auto"; + left: number | "auto"; + right: number | "auto"; + bottom: number | "auto"; + }; value?: number; per?: string; header?: string; + Data: {}; }; type Zone = { @@ -21,7 +28,12 @@ type DroppedObjectsState = { updateObjectPosition: ( zoneName: string, index: number, - newPosition: [number, number] + newPosition: { + top: number | "auto"; + left: number | "auto"; + right: number | "auto"; + bottom: number | "auto"; + } ) => void; }; @@ -64,15 +76,17 @@ export const useDroppedObjectsStore = create((set) => ({ })); export interface DroppedObjects { - header: string; - value: string | number; // ✅ Allows both numbers and formatted strings - per: string; - className: string; - position: [number, number]; // ✅ Ensures position is a tuple - } - - export interface Zones { - zoneName: string; - zoneId: string; - objects: DroppedObject[]; - } \ No newline at end of file + header: string; + id: string; + Data: {}; + value: string | number; // ✅ Allows both numbers and formatted strings + per: string; + className: string; + position: { top: number; left: number; right: number; bottom: number }; // ✅ Ensures position is a tuple +} + +export interface Zones { + zoneName: string; + zoneId: string; + objects: DroppedObject[]; +} From 8fc4453cee904607f07a975bace8ec98bbe170c1 Mon Sep 17 00:00:00 2001 From: Vishnu Date: Mon, 31 Mar 2025 11:11:44 +0530 Subject: [PATCH 23/23] 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; } }