diff --git a/app/package-lock.json b/app/package-lock.json index 7d72590..a820896 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -4138,6 +4138,7 @@ "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", diff --git a/app/src/assets/image/banner.png b/app/src/assets/image/banner.png new file mode 100644 index 0000000..d267ac2 Binary files /dev/null and b/app/src/assets/image/banner.png differ diff --git a/app/src/assets/image/feneration.png b/app/src/assets/image/feneration.png new file mode 100644 index 0000000..c8b6521 Binary files /dev/null and b/app/src/assets/image/feneration.png differ diff --git a/app/src/assets/image/image.png b/app/src/assets/image/image.png index 61d4cdb..34d7e55 100644 Binary files a/app/src/assets/image/image.png and b/app/src/assets/image/image.png differ diff --git a/app/src/assets/image/machines.png b/app/src/assets/image/machines.png new file mode 100644 index 0000000..199870d Binary files /dev/null and b/app/src/assets/image/machines.png differ diff --git a/app/src/assets/image/vehicles.png b/app/src/assets/image/vehicles.png new file mode 100644 index 0000000..5701c94 Binary files /dev/null and b/app/src/assets/image/vehicles.png differ diff --git a/app/src/assets/image/workStation.png b/app/src/assets/image/workStation.png new file mode 100644 index 0000000..6345487 Binary files /dev/null and b/app/src/assets/image/workStation.png differ diff --git a/app/src/assets/image/worker.png b/app/src/assets/image/worker.png new file mode 100644 index 0000000..7067644 Binary files /dev/null and b/app/src/assets/image/worker.png differ diff --git a/app/src/components/icons/DashboardIcon.tsx b/app/src/components/icons/DashboardIcon.tsx new file mode 100644 index 0000000..f1f4d7d --- /dev/null +++ b/app/src/components/icons/DashboardIcon.tsx @@ -0,0 +1,255 @@ +export function NotificationIcon() { + return ( + + + + + ); +} + +export function HomeIcon() { + return ( + + + + ); +} + +export function ProjectsIcon() { + return ( + + + + ); +} + +export function TutorialsIcon() { + return ( + + + + + + + + + + + + + + + + + + + + ); +} + +export function DocumentationIcon() { + return ( + + + + + ); +} + +export function HelpIcon() { + return ( + + + + + + + + + + + ); +} + +export function LogoutIcon() { + return ( + + + + + + ); +} diff --git a/app/src/components/icons/ExportCommonIcons.tsx b/app/src/components/icons/ExportCommonIcons.tsx index d2c9970..6e73c0e 100644 --- a/app/src/components/icons/ExportCommonIcons.tsx +++ b/app/src/components/icons/ExportCommonIcons.tsx @@ -526,21 +526,21 @@ export const KebabIcon = () => { cy="1.35112" rx="1.4993" ry="1.27477" - fill="#E1E1E1" + fill="var(--text-color)" /> ); @@ -559,7 +559,7 @@ export const DublicateIcon = () => { fillRule="evenodd" clipRule="evenodd" d="M14.3125 11.375C14.3125 11.7545 14.0045 12.0625 13.625 12.0625H8.125C7.7455 12.0625 7.4375 11.7545 7.4375 11.375V5.875C7.4375 5.4955 7.7455 5.1875 8.125 5.1875H13.625C14.0045 5.1875 14.3125 5.4955 14.3125 5.875V11.375ZM13.625 4.5H8.125C7.36566 4.5 6.75 5.11566 6.75 5.875V11.375C6.75 12.1343 7.36566 12.75 8.125 12.75H13.625C14.3843 12.75 15 12.1343 15 11.375V5.875C15 5.11566 14.3843 4.5 13.625 4.5ZM11.5625 14.125C11.5625 14.5045 11.2545 14.8125 10.875 14.8125H5.375C4.9955 14.8125 4.6875 14.5045 4.6875 14.125V8.625C4.6875 8.2455 4.9955 7.9375 5.375 7.9375H6.0625V7.25H5.375C4.61566 7.25 4 7.86566 4 8.625V14.125C4 14.8843 4.61566 15.5 5.375 15.5H10.875C11.6343 15.5 12.25 14.8843 12.25 14.125V13.4375H11.5625V14.125Z" - fill="#6F42C1" + fill="var(--text-color)" /> ); @@ -576,31 +576,31 @@ export const DeleteIcon = () => { > diff --git a/app/src/components/icons/ExportToolsIcons.tsx b/app/src/components/icons/ExportToolsIcons.tsx index 9e2c937..478113c 100644 --- a/app/src/components/icons/ExportToolsIcons.tsx +++ b/app/src/components/icons/ExportToolsIcons.tsx @@ -636,3 +636,32 @@ export function SaveTemplateIcon({ isActive }: { isActive: boolean }) { ); } + +export function MeasureToolIcon({ isActive }: { isActive: boolean }) { + return ( + + + + + + + ); +} diff --git a/app/src/components/icons/SimulationIcons.tsx b/app/src/components/icons/SimulationIcons.tsx index 28ce559..4879ea4 100644 --- a/app/src/components/icons/SimulationIcons.tsx +++ b/app/src/components/icons/SimulationIcons.tsx @@ -179,8 +179,8 @@ export function MoveArrowRight() { ); @@ -198,8 +198,8 @@ export function MoveArrowLeft() { ); diff --git a/app/src/components/layout/Dashboard/DashboardCard.tsx b/app/src/components/layout/Dashboard/DashboardCard.tsx new file mode 100644 index 0000000..d58bfb6 --- /dev/null +++ b/app/src/components/layout/Dashboard/DashboardCard.tsx @@ -0,0 +1,25 @@ +import React from "react"; +import { KebabIcon } from "../../icons/ExportCommonIcons"; +import img from "../../../assets/image/image.png" + +const DashboardCard:React.FC = () => { + return ( +
+
+ +
+
+
+
Untitled
+
24-12-2025
+
+
+
V
+ +
+
+
+ ); +}; + +export default DashboardCard; diff --git a/app/src/components/layout/Dashboard/DashboardHome.tsx b/app/src/components/layout/Dashboard/DashboardHome.tsx new file mode 100644 index 0000000..38e7bad --- /dev/null +++ b/app/src/components/layout/Dashboard/DashboardHome.tsx @@ -0,0 +1,21 @@ +import React from "react"; +import DashboardCard from "./DashboardCard"; +import DashboardNavBar from "./DashboardNavBar"; +import MarketPlaceBanner from "./MarketPlaceBanner"; + +const DashboardHome: React.FC = () => { + return ( +
+ + +
+
Recents
+
+ +
+
+
+ ); +}; + +export default DashboardHome; diff --git a/app/src/components/layout/Dashboard/DashboardNavBar.tsx b/app/src/components/layout/Dashboard/DashboardNavBar.tsx new file mode 100644 index 0000000..3da1a61 --- /dev/null +++ b/app/src/components/layout/Dashboard/DashboardNavBar.tsx @@ -0,0 +1,21 @@ +import React from "react"; +import { CartIcon } from "../../icons/ExportModuleIcons"; +import Search from "../../ui/inputs/Search"; + +interface DashboardNavBarProps { + page: React.ReactNode; +} + +const DashboardNavBar: React.FC = ({ page }) => { + return ( +
+
{page}
+
+ Market Place +
+ {}} /> +
+ ); +}; + +export default DashboardNavBar; diff --git a/app/src/components/layout/Dashboard/MarketPlaceBanner.tsx b/app/src/components/layout/Dashboard/MarketPlaceBanner.tsx new file mode 100644 index 0000000..b89bfb2 --- /dev/null +++ b/app/src/components/layout/Dashboard/MarketPlaceBanner.tsx @@ -0,0 +1,44 @@ +import React from "react"; +import banner from "../../../assets/image/banner.png"; + +const MarketPlaceBanner = () => { + return ( +
+ {/* market place banner */} + +
+ NEW +
FALL +
COLLECTION +
+
Unlock Creativity with Premium 3D Assets!
+
+ + + + +
+
Explore Now
+
+ ); +}; + +export default MarketPlaceBanner; diff --git a/app/src/components/layout/Dashboard/SidePannel.tsx b/app/src/components/layout/Dashboard/SidePannel.tsx new file mode 100644 index 0000000..d44c72c --- /dev/null +++ b/app/src/components/layout/Dashboard/SidePannel.tsx @@ -0,0 +1,69 @@ +import React from "react"; +import { + DocumentationIcon, + HelpIcon, + HomeIcon, + LogoutIcon, + NotificationIcon, + ProjectsIcon, + TutorialsIcon, +} from "../../icons/DashboardIcon"; +import { SettingsIcon, TrashIcon } from "../../icons/ExportCommonIcons"; + +const SidePannel: React.FC = () => { + const userName = localStorage.getItem("userName") || "Anonymous"; + return ( +
+
+
+
{userName[0]}
+
{userName}
+
+
+ +
+
+
+ New project
+
+
+
+ + Home +
+
+ + Projects +
+
+ + Trash +
+
+ + Tutorials +
+
+ + Documentation +
+
+
+
+ + Settings +
+
+ + Log out +
+
+ + Help & Feedback +
+
+
+
+ ); +}; + +export default SidePannel; diff --git a/app/src/components/layout/sidebarLeft/Assets.tsx b/app/src/components/layout/sidebarLeft/Assets.tsx index bbf476c..01b8c8e 100644 --- a/app/src/components/layout/sidebarLeft/Assets.tsx +++ b/app/src/components/layout/sidebarLeft/Assets.tsx @@ -1,13 +1,105 @@ import React, { useState } from "react"; import Search from "../../ui/inputs/Search"; - +import vehicle from "../../../assets/image/vehicles.png"; +import workStation from "../../../assets/image/workStation.png"; +import machines from "../../../assets/image/machines.png"; +import feneration from "../../../assets/image/feneration.png"; +import worker from "../../../assets/image/worker.png"; const Assets: React.FC = () => { const [searchValue, setSearchValue] = useState(""); + const [selectedCategory, setSelectedCategory] = useState(null); const handleSearchChange = (value: string) => { setSearchValue(value); - console.log(value); // Log the search value if needed + setSelectedCategory(null); // Reset selected category when search changes }; + + const categoryList = [ + { + assetName: "Doors", + assetImage: "", + category: "Feneration", + categoryImage: feneration, + }, + { + assetName: "Windows", + assetImage: "", + category: "Feneration", + categoryImage: feneration, + }, + { + assetName: "Pillars", + assetImage: "", + category: "Feneration", + categoryImage: feneration, + }, + { + assetName: "Forklift", + assetImage: "", + category: "Vehicles", + categoryImage: vehicle, + }, + { + assetName: "AGV", + assetImage: "", + category: "Vehicles", + categoryImage: vehicle, + }, + { + assetName: "Pallet", + assetImage: "", + category: "Workstation", + categoryImage: workStation, + }, + { + assetName: "Controller", + assetImage: "", + category: "Workstation", + categoryImage: workStation, + }, + { + assetName: "Conveyor", + assetImage: "", + category: "Workstation", + categoryImage: workStation, + }, + { + assetName: "VMC", + assetImage: "", + category: "Machines", + categoryImage: machines, + }, + { + assetName: "CMC", + assetImage: "", + category: "Machines", + categoryImage: machines, + }, + { + assetName: "Male Worker", + assetImage: "", + category: "Workers", + categoryImage: worker, + }, + { + assetName: "Female Worker", + assetImage: "", + category: "Workers", + categoryImage: worker, + }, + ]; + + // Get unique categories + const uniqueCategories = Array.from( + new Set(categoryList.map((asset) => asset.category)) + ); + + // Filter assets based on the selected category + const filteredAssets = + selectedCategory !== null + ? categoryList.filter((asset) => asset.category === selectedCategory) + : []; + return (
@@ -15,11 +107,58 @@ const Assets: React.FC = () => {

Results for "{searchValue}"

+ ) : selectedCategory ? ( +
+ {/* Back Button */} +
setSelectedCategory(null)} + > + ← Back +
+

{selectedCategory}

+
+ {filteredAssets.map((asset, index) => ( +
+ {asset.assetName} +
{asset.assetName}
+
+ ))} +
+
) : ( - <> +
+

Categories

+
+ {uniqueCategories.map((category, index) => { + const categoryInfo = categoryList.find( + (asset) => asset.category === category + ); + return ( +
setSelectedCategory(category)} + > + {category} +
{category}
+
+ ); + })} +
+
)}
); }; export default Assets; + diff --git a/app/src/components/layout/sidebarLeft/Header.tsx b/app/src/components/layout/sidebarLeft/Header.tsx index ce76cb3..06706c6 100644 --- a/app/src/components/layout/sidebarLeft/Header.tsx +++ b/app/src/components/layout/sidebarLeft/Header.tsx @@ -22,7 +22,10 @@ const Header: React.FC = () => {
{ - if (activeModule !== "market") setToggleUI(!toggleUI); + if (activeModule !== "market") { + setToggleUI(!toggleUI); + localStorage.setItem("navBarUi", JSON.stringify(!toggleUI)); + } }} > diff --git a/app/src/components/layout/sidebarLeft/visualization/Templates.tsx b/app/src/components/layout/sidebarLeft/visualization/Templates.tsx index 1f1e61c..ef710f7 100644 --- a/app/src/components/layout/sidebarLeft/visualization/Templates.tsx +++ b/app/src/components/layout/sidebarLeft/visualization/Templates.tsx @@ -5,7 +5,6 @@ const Templates = () => { const { templates, removeTemplate } = useTemplateStore(); const { setSelectedZone } = useSelectedZoneStore(); - console.log("templates: ", templates); const handleDeleteTemplate = (id: string) => { removeTemplate(id); }; 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/Header.tsx b/app/src/components/layout/sidebarRight/Header.tsx index b80fd53..1da29a2 100644 --- a/app/src/components/layout/sidebarRight/Header.tsx +++ b/app/src/components/layout/sidebarRight/Header.tsx @@ -1,49 +1,70 @@ -import React from "react"; +import React, { useEffect, useState } from "react"; import { AppDockIcon } from "../../icons/HeaderIcons"; -import orgImg from "../../../assets/orgTemp.png" +import orgImg from "../../../assets/orgTemp.png"; +import { useActiveUsers } from "../../../store/store"; +import { getAvatarColor } from "../../../modules/collaboration/users/functions/getAvatarColor"; +import { ActiveUser } from "../../../types/users"; +import CollaborationPopup from "../../templates/CollaborationPopup"; const Header: React.FC = () => { - const guestUsers = [ - { value: "Nazria", color: "#43C06D" }, - { value: "Name1", color: "#0050EB" }, - { value: "Abigail", color: "#FF6600" }, - { value: "Jack", color: "#488EF6" }, - ]; // Example guest users array + const { activeUsers } = useActiveUsers(); + const userName = localStorage.getItem("userName") || "Anonymous"; + + const guestUsers: ActiveUser[] = activeUsers.filter( + (user: ActiveUser) => user.userName !== userName + ); + + const [userManagement, setUserManagement] = useState(false); return ( -
-
-
Share
-
- + <> + {userManagement && ( + + )} +
+
+
{ + setUserManagement(true); + }} + > + Share +
+
+ +
-
-
-
-
- {guestUsers.length > 3 && ( -
+{guestUsers.length - 3}
- )} - {guestUsers.slice(0, 3).map((user, index) => ( +
+
+
+ {guestUsers.length > 3 && ( +
+{guestUsers.length - 3}
+ )} + {guestUsers.slice(0, 3).map((user, index) => ( +
+ {user.userName[0]} +
+ ))} +
+
- {user.value[0]} + {userName[0]} +
+
+
- ))} -
-
-
- V -
-
-
-
+ ); }; diff --git a/app/src/components/layout/sidebarRight/SideBarRight.tsx b/app/src/components/layout/sidebarRight/SideBarRight.tsx index 3ba5595..4821772 100644 --- a/app/src/components/layout/sidebarRight/SideBarRight.tsx +++ b/app/src/components/layout/sidebarRight/SideBarRight.tsx @@ -14,7 +14,10 @@ import ConveyorMechanics from "./mechanics/ConveyorMechanics"; import Visualization from "./visualization/Visualization"; import Analysis from "./analysis/Analysis"; import Simulations from "./simulation/Simulations"; -import { useSelectedActionSphere } from "../../../store/store"; +import { + useSelectedActionSphere, + useselectedFloorItem, +} from "../../../store/store"; import GlobalProperties from "./properties/GlobalProperties"; import AsstePropertiies from "./properties/AssetProperties"; import ZoneProperties from "./properties/ZoneProperties"; @@ -25,6 +28,7 @@ const SideBarRight: React.FC = () => { const { toggleUI } = useToggleStore(); const { selectedActionSphere } = useSelectedActionSphere(); const { subModule, setSubModule } = useSubModuleStore(); + const { selectedFloorItem } = useselectedFloorItem(); // Reset activeList whenever activeModule changes useEffect(() => { if (activeModule !== "simulation") setSubModule("properties"); @@ -38,8 +42,9 @@ const SideBarRight: React.FC = () => {
{/* {activeModule === "builder" && ( */}
setSubModule("properties")} > @@ -48,22 +53,25 @@ const SideBarRight: React.FC = () => { {activeModule === "simulation" && ( <>
setSubModule("mechanics")} >
setSubModule("simulations")} >
setSubModule("analysis")} > @@ -75,12 +83,21 @@ const SideBarRight: React.FC = () => { {/* process builder */} {toggleUI && subModule === "properties" && - activeModule !== "visualization" && ( + activeModule !== "visualization" && + !selectedFloorItem && (
- {/* */} - {/* */} +
+
+ )} + {toggleUI && + subModule === "properties" && + activeModule !== "visualization" && + selectedFloorItem && ( +
+
+
)} @@ -89,9 +106,7 @@ const SideBarRight: React.FC = () => { activeModule === "builder" && (
- {/* */} - {/* */}
)} @@ -99,20 +114,24 @@ const SideBarRight: React.FC = () => { {toggleUI && activeModule === "simulation" && ( <> - {subModule === "mechanics" && selectedActionSphere && selectedActionSphere.path.type === "Conveyor" && ( -
-
- + {subModule === "mechanics" && + selectedActionSphere && + selectedActionSphere.path.type === "Conveyor" && ( +
+
+ +
-
- )} - {subModule === "mechanics" && selectedActionSphere && selectedActionSphere.path.type === "Vehicle" && ( -
-
- {/* */} + )} + {subModule === "mechanics" && + selectedActionSphere && + selectedActionSphere.path.type === "Vehicle" && ( +
+
+ +
-
- )} + )} {subModule === "mechanics" && !selectedActionSphere && (
diff --git a/app/src/components/layout/sidebarRight/customInput/PositionInputs.tsx b/app/src/components/layout/sidebarRight/customInput/PositionInputs.tsx index b65d782..881e225 100644 --- a/app/src/components/layout/sidebarRight/customInput/PositionInputs.tsx +++ b/app/src/components/layout/sidebarRight/customInput/PositionInputs.tsx @@ -4,12 +4,16 @@ interface PositionInputProps { onChange: (value: string) => void; // Callback for value change placeholder?: string; // Optional placeholder type?: string; // Input type (e.g., text, number, email) + value1?: number; + value2?: number; } const PositionInput: React.FC = ({ onChange, placeholder = "Enter value", // Default placeholder type = "number", // Default type + value1 = "number", + value2 = "number", }) => { return (
@@ -22,6 +26,7 @@ const PositionInput: React.FC = ({ type={type} onChange={(e) => onChange(e.target.value)} placeholder={placeholder} + value={value2} />
@@ -31,6 +36,7 @@ const PositionInput: React.FC = ({ type={type} onChange={(e) => onChange(e.target.value)} placeholder={placeholder} + value={value1} />
diff --git a/app/src/components/layout/sidebarRight/customInput/RotationInput.tsx b/app/src/components/layout/sidebarRight/customInput/RotationInput.tsx index 3ab01a4..962e967 100644 --- a/app/src/components/layout/sidebarRight/customInput/RotationInput.tsx +++ b/app/src/components/layout/sidebarRight/customInput/RotationInput.tsx @@ -4,17 +4,19 @@ interface RotationInputProps { onChange: (value: string) => void; // Callback for value change placeholder?: string; // Optional placeholder type?: string; // Input type (e.g., text, number, email) + value?: number; } const RotationInput: React.FC = ({ onChange, placeholder = "Enter value", // Default placeholder type = "number", // Default type + value = "number", }) => { return (
Rotation
-
+
Rotate :
= ({ type={type} onChange={(e) => onChange(e.target.value)} placeholder={placeholder} + value={value} />
diff --git a/app/src/components/layout/sidebarRight/mechanics/ConveyorMechanics.tsx b/app/src/components/layout/sidebarRight/mechanics/ConveyorMechanics.tsx index a1f8bf3..f80fca0 100644 --- a/app/src/components/layout/sidebarRight/mechanics/ConveyorMechanics.tsx +++ b/app/src/components/layout/sidebarRight/mechanics/ConveyorMechanics.tsx @@ -8,9 +8,7 @@ import { import RenameInput from "../../../ui/inputs/RenameInput"; import InputWithDropDown from "../../../ui/inputs/InputWithDropDown"; import LabledDropdown from "../../../ui/inputs/LabledDropdown"; -import RegularDropDown from "../../../ui/inputs/RegularDropDown"; import { handleResize } from "../../../../functions/handleResizePannel"; -import EyeDropInput from "../../../ui/inputs/EyeDropInput"; import { useSelectedActionSphere, useSelectedPath, useSimulationPaths } from "../../../../store/store"; import * as THREE from 'three'; import * as Types from '../../../../types/world/worldTypes'; @@ -218,7 +216,7 @@ const ConveyorMechanics: React.FC = () => { setSimulationPaths(updatedPaths); }; - const handleSpeedChange = (speed: number) => { + const handleSpeedChange = (speed: number | string) => { if (!selectedPath) return; const updatedPaths = simulationPaths.map((path) => @@ -243,6 +241,7 @@ const ConveyorMechanics: React.FC = () => { uuid: THREE.MathUtils.generateUUID(), name: `Trigger ${triggerIndex + 1}`, type: '', + bufferTime: 0, isUsed: false }; @@ -298,8 +297,19 @@ const ConveyorMechanics: React.FC = () => { ); setSimulationPaths(updatedPaths); + + // Ensure the selectedItem is updated immediately + const updatedTrigger = updatedPaths + .flatMap((path) => (path.type === "Conveyor" ? path.points : [])) + .flatMap((point) => point.triggers) + .find((trigger) => trigger.uuid === uuid); + + if (updatedTrigger) { + setSelectedItem({ type: "trigger", item: updatedTrigger }); + } }; + // Update the toggle handlers to immediately update the selected item const handleActionToggle = (uuid: string) => { if (!selectedActionSphere) return; @@ -373,17 +383,61 @@ const ConveyorMechanics: React.FC = () => { } }; + const handleTriggerBufferTimeChange = (uuid: string, bufferTime: number) => { + if (!selectedActionSphere) return; + + const updatedPaths = simulationPaths.map((path) => + path.type === "Conveyor" + ? { + ...path, + points: path.points.map((point) => + point.uuid === selectedActionSphere.point.uuid + ? { + ...point, + triggers: point.triggers.map((trigger) => + trigger.uuid === uuid ? { ...trigger, bufferTime } : trigger + ), + } + : point + ), + } + : path + ); + + setSimulationPaths(updatedPaths); + + // Immediately update selectedItem if it's the currently selected trigger + if (selectedItem?.type === "trigger" && selectedItem.item.uuid === uuid) { + setSelectedItem({ + ...selectedItem, + item: { + ...selectedItem.item, + bufferTime + } + }); + } + }; + const [selectedItem, setSelectedItem] = useState<{ type: "action" | "trigger"; item: any; } | null>(null); useEffect(() => { - setSelectedItem(null); // Reset selectedItem when selectedActionSphere changes + setSelectedItem(null); }, [selectedActionSphere]); return (
-
- {selectedActionSphere?.path?.modelName || "point name not found"} -
+ {!selectedPath && +
+ {selectedActionSphere?.path?.modelName || "point name not found"} +
+ } + + {selectedPath && + +
+ {selectedPath.path.modelName || "path name not found"} +
+ }
{!selectedPath && @@ -559,25 +613,45 @@ const ConveyorMechanics: React.FC = () => { options={["On-Hit", "Buffer"]} onSelect={(option) => handleTriggerSelect(selectedItem.item.uuid, option)} /> + + {selectedItem.item.type === "Buffer" && ( + { + handleTriggerBufferTimeChange(selectedItem.item.uuid, parseInt(value)); + }} + /> + )} )} + )} {selectedPath && !selectedItem && ( -
+
handleSpeedChange(parseFloat(value))} + min={0} + value={selectedPath.path.speed === "Inherit" ? "" : selectedPath.path.speed.toString()} + onChange={(value) => handleSpeedChange((value === "") ? "Inherit" : parseInt(value))} />
)}
-
- - By selecting points, you can create events and triggers. -
+ {!selectedPath && ( +
+ + Configure the point's action and trigger properties. +
+ )} + {selectedPath && ( +
+ + Configure the path properties. +
+ )}
); diff --git a/app/src/components/layout/sidebarRight/mechanics/VehicleMechanics.tsx b/app/src/components/layout/sidebarRight/mechanics/VehicleMechanics.tsx index 7d3ffb0..c611a13 100644 --- a/app/src/components/layout/sidebarRight/mechanics/VehicleMechanics.tsx +++ b/app/src/components/layout/sidebarRight/mechanics/VehicleMechanics.tsx @@ -1,561 +1,158 @@ -import React, { useRef, useState, useMemo, useEffect } from "react"; -import { - AddIcon, - InfoIcon, - RemoveIcon, - ResizeHeightIcon, -} from "../../../icons/ExportCommonIcons"; -import RenameInput from "../../../ui/inputs/RenameInput"; +import React, { useRef, useMemo } from "react"; +import { InfoIcon } from "../../../icons/ExportCommonIcons"; import InputWithDropDown from "../../../ui/inputs/InputWithDropDown"; -import LabledDropdown from "../../../ui/inputs/LabledDropdown"; -import RegularDropDown from "../../../ui/inputs/RegularDropDown"; -import { handleResize } from "../../../../functions/handleResizePannel"; -import EyeDropInput from "../../../ui/inputs/EyeDropInput"; -import { useSelectedActionSphere, useSelectedPath, useSimulationPaths } from "../../../../store/store"; -import * as THREE from 'three'; +import { useSelectedActionSphere, useSimulationPaths } from "../../../../store/store"; import * as Types from '../../../../types/world/worldTypes'; -import InputToggle from "../../../ui/inputs/InputToggle"; +import LabledDropdown from "../../../ui/inputs/LabledDropdown"; const VehicleMechanics: React.FC = () => { const { selectedActionSphere } = useSelectedActionSphere(); - const { selectedPath, setSelectedPath } = useSelectedPath(); const { simulationPaths, setSimulationPaths } = useSimulationPaths(); - const actionsContainerRef = useRef(null); - const triggersContainerRef = useRef(null); + const propertiesContainerRef = useRef(null); - const selectedPoint = useMemo(() => { - if (!selectedActionSphere) return null; - return simulationPaths - .filter((path): path is Types.ConveyorEventsSchema => path.type === "Conveyor") - .flatMap((path) => path.points) - .find((point) => point.uuid === selectedActionSphere.point.uuid); + const { selectedPoint, connectedPointUuids } = useMemo(() => { + if (!selectedActionSphere?.point?.uuid) return { selectedPoint: null, connectedPointUuids: [] }; + + const vehiclePaths = simulationPaths.filter( + (path): path is Types.VehicleEventsSchema => path.type === "Vehicle" + ); + + const point = vehiclePaths.find( + (path) => path.point.uuid === selectedActionSphere.point.uuid + )?.point; + + 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 handleAddAction = () => { - if (!selectedActionSphere) return; + const handleActionUpdate = React.useCallback((updatedAction: Partial) => { + if (!selectedActionSphere?.point?.uuid) return; const updatedPaths = simulationPaths.map((path) => { - if (path.type === "Conveyor") { + if (path.type === "Vehicle" && path.point.uuid === selectedActionSphere.point.uuid) { return { ...path, - points: path.points.map((point) => { - if (point.uuid === selectedActionSphere.point.uuid) { - const actionIndex = point.actions.length; - const newAction = { - uuid: THREE.MathUtils.generateUUID(), - name: `Action ${actionIndex + 1}`, - type: 'Inherit', - material: 'Inherit', - delay: 'Inherit', - spawnInterval: 'Inherit', - isUsed: false - }; - - return { ...point, actions: [...point.actions, newAction] }; + point: { + ...path.point, + actions: { + ...path.point.actions, + ...updatedAction } - return point; - }), + } }; } return path; }); setSimulationPaths(updatedPaths); - }; + }, [selectedActionSphere?.point?.uuid, simulationPaths, setSimulationPaths]); - const handleDeleteAction = (uuid: string) => { - if (!selectedActionSphere) return; + const handleStartPointChange = React.useCallback((uuid: string) => { + handleActionUpdate({ start: uuid }); + }, [handleActionUpdate]); - const updatedPaths = simulationPaths.map((path) => - path.type === "Conveyor" - ? { + const handleEndPointChange = React.useCallback((uuid: string) => { + handleActionUpdate({ end: uuid }); + }, [handleActionUpdate]); + + const handleHitCountChange = React.useCallback((hitCount: number) => { + handleActionUpdate({ hitCount }); + }, [handleActionUpdate]); + + const handleBufferChange = React.useCallback((buffer: number) => { + handleActionUpdate({ buffer }); + }, [handleActionUpdate]); + + const handleSpeedChange = React.useCallback((speed: number) => { + if (!selectedActionSphere?.point?.uuid) return; + + const updatedPaths = simulationPaths.map((path) => { + if (path.type === "Vehicle" && path.point.uuid === selectedActionSphere.point.uuid) { + return { ...path, - points: path.points.map((point) => - point.uuid === selectedActionSphere.point.uuid - ? { ...point, actions: point.actions.filter(action => action.uuid !== uuid) } - : point - ), - } - : path - ); - - setSimulationPaths(updatedPaths); - }; - - const handleActionSelect = (uuid: string, actionType: string) => { - if (!selectedActionSphere) return; - - const updatedPaths = simulationPaths.map((path) => - path.type === "Conveyor" - ? { - ...path, - points: path.points.map((point) => - point.uuid === selectedActionSphere.point.uuid - ? { - ...point, - actions: point.actions.map((action) => - action.uuid === uuid - ? { - ...action, - type: actionType, - material: actionType === 'Spawn' || actionType === 'Swap' ? 'Inherit' : action.material, - delay: actionType === 'Delay' ? 'Inherit' : action.delay, - spawnInterval: actionType === 'Spawn' ? 'Inherit' : action.spawnInterval - } - : action - ), - } - : point - ), - } - : path - ); - - setSimulationPaths(updatedPaths); - - // Update the selected item to reflect changes - if (selectedItem?.type === "action" && selectedItem.item.uuid === uuid) { - const updatedAction = updatedPaths - .filter((path): path is Types.ConveyorEventsSchema => path.type === "Conveyor") - .flatMap(path => path.points) - .find(p => p.uuid === selectedActionSphere.point.uuid) - ?.actions.find(a => a.uuid === uuid); - - if (updatedAction) { - setSelectedItem({ - type: "action", - item: updatedAction - }); + point: { + ...path.point, + speed: speed + } + }; } - } - }; - - // Modified handleMaterialSelect to ensure it only applies to relevant action types - const handleMaterialSelect = (uuid: string, material: string) => { - if (!selectedActionSphere) return; - - const updatedPaths = simulationPaths.map((path) => - path.type === "Conveyor" - ? { - ...path, - points: path.points.map((point) => - point.uuid === selectedActionSphere.point.uuid - ? { - ...point, - actions: point.actions.map((action) => - action.uuid === uuid && - (action.type === 'Spawn' || action.type === 'Swap') - ? { ...action, material } - : action - ), - } - : point - ), - } - : path - ); + return path; + }); setSimulationPaths(updatedPaths); - - // Update selected item if it's the current action - if (selectedItem?.type === "action" && selectedItem.item.uuid === uuid) { - setSelectedItem({ - ...selectedItem, - item: { - ...selectedItem.item, - material - } - }); - } - }; - - const handleDelayChange = (uuid: string, delay: number | string) => { - if (!selectedActionSphere) return; - - const updatedPaths = simulationPaths.map((path) => - path.type === "Conveyor" - ? { - ...path, - points: path.points.map((point) => - point.uuid === selectedActionSphere.point.uuid - ? { - ...point, - actions: point.actions.map((action) => - action.uuid === uuid ? { ...action, delay } : action - ), - } - : point - ), - } - : path - ); - - setSimulationPaths(updatedPaths); - }; - - const handleSpawnIntervalChange = (uuid: string, spawnInterval: number | string) => { - if (!selectedActionSphere) return; - - const updatedPaths = simulationPaths.map((path) => - path.type === "Conveyor" - ? { - ...path, - points: path.points.map((point) => - point.uuid === selectedActionSphere.point.uuid - ? { - ...point, - actions: point.actions.map((action) => - action.uuid === uuid ? { ...action, spawnInterval } : action - ), - } - : point - ), - } - : path - ); - - setSimulationPaths(updatedPaths); - }; - - const handleSpeedChange = (speed: number) => { - if (!selectedPath) return; - - const updatedPaths = simulationPaths.map((path) => - path.modeluuid === selectedPath.path.modeluuid ? { ...path, speed } : path - ); - - setSimulationPaths(updatedPaths); - setSelectedPath({ ...selectedPath, path: { ...selectedPath.path, speed } }); - }; - - const handleAddTrigger = () => { - if (!selectedActionSphere) return; - - const updatedPaths = simulationPaths.map((path) => - path.type === "Conveyor" - ? { - ...path, - points: path.points.map((point) => { - if (point.uuid === selectedActionSphere.point.uuid) { - const triggerIndex = point.triggers.length; - const newTrigger = { - uuid: THREE.MathUtils.generateUUID(), - name: `Trigger ${triggerIndex + 1}`, - type: '', - isUsed: false - }; - - return { ...point, triggers: [...point.triggers, newTrigger] }; - } - return point; - }), - } - : path - ); - - setSimulationPaths(updatedPaths); - }; - - const handleDeleteTrigger = (uuid: string) => { - if (!selectedActionSphere) return; - - const updatedPaths = simulationPaths.map((path) => - path.type === "Conveyor" - ? { - ...path, - points: path.points.map((point) => - point.uuid === selectedActionSphere.point.uuid - ? { ...point, triggers: point.triggers.filter(trigger => trigger.uuid !== uuid) } - : point - ), - } - : path - ); - - setSimulationPaths(updatedPaths); - }; - - const handleTriggerSelect = (uuid: string, triggerType: string) => { - if (!selectedActionSphere) return; - - const updatedPaths = simulationPaths.map((path) => - path.type === "Conveyor" - ? { - ...path, - points: path.points.map((point) => - point.uuid === selectedActionSphere.point.uuid - ? { - ...point, - triggers: point.triggers.map((trigger) => - trigger.uuid === uuid ? { ...trigger, type: triggerType } : trigger - ), - } - : point - ), - } - : path - ); - - setSimulationPaths(updatedPaths); - }; - - // Update the toggle handlers to immediately update the selected item - const handleActionToggle = (uuid: string) => { - if (!selectedActionSphere) return; - const updatedPaths = simulationPaths.map((path) => - path.type === "Conveyor" - ? { - ...path, - points: path.points.map((point) => - point.uuid === selectedActionSphere.point.uuid - ? { - ...point, - actions: point.actions.map((action) => ({ - ...action, - isUsed: action.uuid === uuid ? !action.isUsed : false, - })), - } - : point - ), - } - : path - ); - - setSimulationPaths(updatedPaths); - - // Immediately update the selected item if it's the one being toggled - if (selectedItem?.type === "action" && selectedItem.item.uuid === uuid) { - setSelectedItem({ - ...selectedItem, - item: { - ...selectedItem.item, - isUsed: !selectedItem.item.isUsed - } - }); - } - }; - - // Do the same for trigger toggle - const handleTriggerToggle = (uuid: string) => { - if (!selectedActionSphere) return; - - const updatedPaths = simulationPaths.map((path) => - path.type === "Conveyor" - ? { - ...path, - points: path.points.map((point) => - point.uuid === selectedActionSphere.point.uuid - ? { - ...point, - triggers: point.triggers.map((trigger) => ({ - ...trigger, - isUsed: trigger.uuid === uuid ? !trigger.isUsed : false, - })), - } - : point - ), - } - : path - ); - - setSimulationPaths(updatedPaths); - - // Immediately update the selected item if it's the one being toggled - if (selectedItem?.type === "trigger" && selectedItem.item.uuid === uuid) { - setSelectedItem({ - ...selectedItem, - item: { - ...selectedItem.item, - isUsed: !selectedItem.item.isUsed - } - }); - } - }; - - const [selectedItem, setSelectedItem] = useState<{ type: "action" | "trigger"; item: any; } | null>(null); - - useEffect(() => { - setSelectedItem(null); // Reset selectedItem when selectedActionSphere changes - }, [selectedActionSphere]); + }, [selectedActionSphere?.point?.uuid, simulationPaths, setSimulationPaths]); return ( -
+
- {selectedActionSphere?.path?.modelName || "point name not found"} + {selectedActionSphere?.path?.modelName || "Vehicle point not found"}
-
-
-
Actions
-
- Add -
-
-
-
- <> - {console.log(selectedPoint)} - -
-
handleResize(e, actionsContainerRef)} - > - -
-
-
-
-
-
Triggers
-
- Add -
-
-
-
- {selectedPoint?.triggers.map((trigger) => ( -
-
setSelectedItem({ type: "trigger", item: trigger })} - > - -
-
handleDeleteTrigger(trigger.uuid)} - > - -
-
- ))} -
-
handleResize(e, triggersContainerRef)} - > - -
-
-
-
- {selectedItem && ( +
+
Vehicle Properties
+ + {selectedPoint && ( <> -
{selectedItem.item.name}
+ - {selectedItem.type === "action" && ( - <> - handleActionToggle(selectedItem.item.uuid)} - /> - handleActionSelect(selectedItem.item.uuid, option)} - /> + - {/* Only show material dropdown for Spawn/Swap actions */} - {(selectedItem.item.type === 'Spawn' || selectedItem.item.type === 'Swap') && ( - handleMaterialSelect(selectedItem.item.uuid, option)} - /> - )} - - {/* Only show delay input for Delay actions */} - {selectedItem.item.type === 'Delay' && ( - { - const numValue = parseInt(value); - handleDelayChange( - selectedItem.item.uuid, - !value ? 'Inherit' : numValue - ); - }} - /> - )} - - {/* Only show spawn interval for Spawn actions */} - {selectedItem.item.type === 'Spawn' && ( - { - handleSpawnIntervalChange(selectedItem.item.uuid, (value === "") ? "Inherit" : parseInt(value)); - }} - /> - - )} - - )} - - {selectedItem.type === "trigger" && ( - <> - handleTriggerToggle(selectedItem.item.uuid)} - /> - - handleTriggerSelect(selectedItem.item.uuid, option)} - /> - - )} - - )} - - {selectedPath && !selectedItem && ( -
handleHitCountChange(parseInt(value))} + /> + + handleBufferChange(parseInt(value))} + /> + + handleSpeedChange(parseFloat(value))} /> -
+ )}
+
- By selecting points, you can create events and triggers. + Configure vehicle's movement and interaction properties.
); }; -export default VehicleMechanics; \ No newline at end of file +export default React.memo(VehicleMechanics); \ No newline at end of file diff --git a/app/src/components/layout/sidebarRight/properties/AssetProperties.tsx b/app/src/components/layout/sidebarRight/properties/AssetProperties.tsx index 8309024..c47fd81 100644 --- a/app/src/components/layout/sidebarRight/properties/AssetProperties.tsx +++ b/app/src/components/layout/sidebarRight/properties/AssetProperties.tsx @@ -1,9 +1,11 @@ -import React, { useState } from "react"; +import React, { useEffect, useState } from "react"; import InputToggle from "../../../ui/inputs/InputToggle"; import InputWithDropDown from "../../../ui/inputs/InputWithDropDown"; import { RemoveIcon } from "../../../icons/ExportCommonIcons"; import PositionInput from "../customInput/PositionInputs"; import RotationInput from "../customInput/RotationInput"; +import { useselectedFloorItem } from "../../../../store/store"; +import * as THREE from "three"; interface UserData { id: number; // Unique identifier for the user data @@ -14,7 +16,13 @@ interface UserData { const AssetProperties: React.FC = () => { const [userData, setUserData] = useState([]); // State to track user data const [nextId, setNextId] = useState(1); // Unique ID for new entries + const { selectedFloorItem } = useselectedFloorItem(); + let xValue = selectedFloorItem.position.x; + let zValue = selectedFloorItem.position.z; + let rotationRad = selectedFloorItem.rotation.y; + let rotationDeg = THREE.MathUtils.radToDeg(rotationRad); + // useEffect(() => {}, [selectedFloorItem]); // Function to handle adding new user data const handleAddUserData = () => { const newUserData: UserData = { @@ -45,12 +53,16 @@ const AssetProperties: React.FC = () => { return (
{/* Name */} -
Selected Object
+
{selectedFloorItem.userData.name}
- { }} /> - { }} /> + {}} + value1={xValue.toFixed(5)} + value2={zValue.toFixed(5)} + /> + {}} value={rotationDeg} />
diff --git a/app/src/components/layout/sidebarRight/properties/GlobalProperties.tsx b/app/src/components/layout/sidebarRight/properties/GlobalProperties.tsx index 672d847..633b144 100644 --- a/app/src/components/layout/sidebarRight/properties/GlobalProperties.tsx +++ b/app/src/components/layout/sidebarRight/properties/GlobalProperties.tsx @@ -3,8 +3,31 @@ import InputRange from "../../../ui/inputs/InputRange"; import InputToggle from "../../../ui/inputs/InputToggle"; import { AI_Icon } from "../../../icons/ExportCommonIcons"; import LabeledButton from "../../../ui/inputs/LabledButton"; +import { + useAzimuth, + useElevation, + useRenderDistance, + useResetCamera, + useRoofVisibility, + useSelectedWallItem, + useShadows, + useSocketStore, + useToggleView, + useWallVisibility, +} from "../../../../store/store"; +import { setEnvironment } from "../../../../services/factoryBuilder/environment/setEnvironment"; const GlobalProperties: React.FC = () => { + const { toggleView, setToggleView } = useToggleView(); + const { selectedWallItem, setSelectedWallItem } = useSelectedWallItem(); + const { roofVisibility, setRoofVisibility } = useRoofVisibility(); + const { wallVisibility, setWallVisibility } = useWallVisibility(); + const { shadows, setShadows } = useShadows(); + const { resetCamera, setResetCamera } = useResetCamera(); + const { elevation, setElevation } = useElevation(); + const { azimuth, setAzimuth } = useAzimuth(); + const { renderDistance, setRenderDistance } = useRenderDistance(); + const { socket } = useSocketStore(); const [limitDistance, setLimitDistance] = useState(false); const [distance, setDistance] = useState(5); @@ -23,6 +46,93 @@ const GlobalProperties: React.FC = () => { function updateGridDistance(value: number) { setGridDistance(value); } + // Function to toggle roof visibility + const changeRoofVisibility = async () => { + const email = localStorage.getItem("email"); + const organization = email!.split("@")[1].split(".")[0]; + + //using REST + const data = await setEnvironment( + organization, + localStorage.getItem("userId")!, + wallVisibility, + !roofVisibility, + shadows + ); + // console.log('data: ', data); + + //using Socket + // const visData = { + // organization: organization, + // userId: localStorage.getItem('userId')!, + // wallVisibility: wallVisibility, + // roofVisibility: !roofVisibility, + // shadowVisibility: shadows, + // socketId: socket.id + // }; + // socket.emit('v1:Environment:set', visData) + + setRoofVisibility(!roofVisibility); // Toggle roof visibility + }; + // Function to toggle wall visibility + const changeWallVisibility = async () => { + const email = localStorage.getItem("email"); + const organization = email!.split("@")[1].split(".")[0]; + //using REST + const data = await setEnvironment( + organization, + localStorage.getItem("userId")!, + !wallVisibility, + roofVisibility, + shadows + ); + // console.log('data: ', data); + + //using Socket + // const visData = { + // organization: organization, + // userId: localStorage.getItem('userId')!, + // wallVisibility: !wallVisibility, + // roofVisibility: roofVisibility, + // shadowVisibility: shadows, + // socketId: socket.id + // }; + // socket.emit('v1:Environment:set', visData) + + setWallVisibility(!wallVisibility); // Toggle wall visibility + }; + + const shadowVisibility = async () => { + const email = localStorage.getItem("email"); + const organization = email!.split("@")[1].split(".")[0]; + //using REST + const data = await setEnvironment( + organization, + localStorage.getItem("userId")!, + wallVisibility, + roofVisibility, + !shadows + ); + // console.log('data: ', data); + + //using Socket + // const visData = { + // organization: organization, + // userId: localStorage.getItem('userId')!, + // wallVisibility: wallVisibility, + // roofVisibility: roofVisibility, + // shadowVisibility: !shadows, + // socketId: socket.id + // }; + // socket.emit('v1:Environment:set', visData) + + setShadows(!shadows); + }; + const toggleResetCamera = () => { + if (!toggleView) { + setResetCamera(true); // Trigger reset camera action + } + }; return (
@@ -34,10 +144,29 @@ const GlobalProperties: React.FC = () => {
- - - - {}} value="Reset"/> + + + +
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/layout/sidebarRight/visualization/IotInputCards/FlotingWidgetInput.tsx b/app/src/components/layout/sidebarRight/visualization/IotInputCards/FlotingWidgetInput.tsx new file mode 100644 index 0000000..5938da9 --- /dev/null +++ b/app/src/components/layout/sidebarRight/visualization/IotInputCards/FlotingWidgetInput.tsx @@ -0,0 +1,177 @@ +import React, { useEffect, useState } from "react"; +import MultiLevelDropdown from "../../../../ui/inputs/MultiLevelDropDown"; +import { AddIcon } from "../../../../icons/ExportCommonIcons"; +import RegularDropDown from "../../../../ui/inputs/RegularDropDown"; +import useChartStore from "../../../../../store/useChartStore"; +import { useSelectedZoneStore } from "../../../../../store/useZoneStore"; +import { useWidgetStore } from "../../../../../store/useWidgetStore"; +import axios from "axios"; +import RenameInput from "../../../../ui/inputs/RenameInput"; + +type Props = {}; + +const FlotingWidgetInput = (props: Props) => { + const [widgetName, setWidgetName] = useState('Widget'); + const { setMeasurements, updateDuration, updateName } = useChartStore(); + const [duration, setDuration] = useState('1h') + const [dropDowndata, setDropDownData] = useState({}); + const [selections, setSelections] = useState>({}); + const { selectedZone } = useSelectedZoneStore(); + const { selectedChartId } = useWidgetStore(); + const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL; + const email = localStorage.getItem("email") || ""; + const organization = email?.split("@")[1]?.split(".")[0] + + useEffect(() => { + const fetchZoneData = async () => { + try { + const response = await axios.get(`http://${iotApiUrl}/getinput`); + if (response.status === 200) { + // console.log("dropdown data:", response.data); + setDropDownData(response.data); + } else { + console.log("Unexpected response:", response); + } + } catch (error) { + console.error("There was an error!", error); + } + }; + fetchZoneData(); + }, []); + + useEffect(() => { + const fetchSavedInputes = async () => { + if (selectedChartId.id !== "") { + try { + const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/WidgetData/${selectedChartId.id}/${organization}`); + if (response.status === 200) { + setSelections(response.data.Data.measurements) + setDuration(response.data.Data.duration) + setWidgetName(response.data.widgetName) + } else { + console.log("Unexpected response:", response); + } + } catch (error) { + console.error("There was an error!", error); + } + } + } + + fetchSavedInputes(); + + }, [selectedChartId.id]); + + // Sync Zustand state when component mounts + useEffect(() => { + setMeasurements(selections); + updateDuration(duration); + updateName(widgetName); + }, [selections, duration, widgetName]); + + + const sendInputes = async (inputMeasurement: any, inputDuration: any, inputName: any) => { + try { + const response = await axios.post(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/floatwidget/save`, { + organization: organization, + zoneId: selectedZone.zoneId, + widget: { + id: selectedChartId.id, + panel: selectedChartId.panel, + widgetName: inputName, + Data: { + measurements: inputMeasurement, + duration: inputDuration + } + } + } as any); + if (response.status === 200) { + return true + } else { + console.log("Unexpected response:", response); + return false + } + } catch (error) { + console.error("There was an error!", error); + return false + } + } + + const handleSelect = async (inputKey: string, selectedData: { name: string; fields: string } | null) => { + + // async() => { + const newSelections = { ...selections }; + if (selectedData === null) { + delete newSelections[inputKey]; + } else { + newSelections[inputKey] = selectedData; + } + // setMeasurements(newSelections); // Update Zustand store + console.log(newSelections); + if (await sendInputes(newSelections, duration, widgetName)) { + setSelections(newSelections); + } + // sendInputes(newSelections, duration); // Send data to server + // return newSelections; + // }; + }; + + const handleSelectDuration = async (option: string) => { + if (await sendInputes(selections, option, widgetName)) { + setDuration(option); + } + // setDuration(option); + }; + + const handleNameChange = async (name:any) => { + console.log('name change requested',name); + + if (await sendInputes(selections, duration, name)) { + setWidgetName(name); + } + } + + return ( + <> +
+
+
Title
+ +
+ {[...Array(6)].map((_, index) => { + const inputKey = `input${index + 1}`; + return ( +
+
Input {index + 1}
+
+ handleSelect(inputKey, selectedData)} + onUnselect={() => handleSelect(inputKey, null)} + selectedValue={selections[inputKey]} // Load from Zustand + /> +
+ +
+
+
+ ); + })} +
+
+
+
Duration
+
+ +
+
+
+ + ); +}; + +export default FlotingWidgetInput; diff --git a/app/src/components/layout/sidebarRight/visualization/IotInputCards/LineGrapInput.tsx b/app/src/components/layout/sidebarRight/visualization/IotInputCards/LineGrapInput.tsx index f36207e..32a5590 100644 --- a/app/src/components/layout/sidebarRight/visualization/IotInputCards/LineGrapInput.tsx +++ b/app/src/components/layout/sidebarRight/visualization/IotInputCards/LineGrapInput.tsx @@ -162,7 +162,7 @@ const LineGrapInput = (props: Props) => { const fetchSavedInputes = async () => { if (selectedChartId.id !== "") { try { - const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_LOCAL_BASE_URL}/api/v2/WidgetData/${selectedChartId.id}/${organization}`); + const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/WidgetData/${selectedChartId.id}/${organization}`); if (response.status === 200) { setSelections(response.data.Data.measurements) setDuration(response.data.Data.duration) @@ -190,7 +190,7 @@ const LineGrapInput = (props: Props) => { const sendInputes = async (inputMeasurement: any, inputDuration: any, inputName: any) => { try { - const response = await axios.post(`http://${process.env.REACT_APP_SERVER_REST_API_LOCAL_BASE_URL}/api/v2/widget/save`, { + const response = await axios.post(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/widget/save`, { organization: organization, zoneId: selectedZone.zoneId, widget: { @@ -253,7 +253,7 @@ const LineGrapInput = (props: Props) => { <>
-
Widget name
+
Title
{[...Array(6)].map((_, index) => { diff --git a/app/src/components/layout/sidebarRight/visualization/data/Data.tsx b/app/src/components/layout/sidebarRight/visualization/data/Data.tsx index 1100954..2c9b5c6 100644 --- a/app/src/components/layout/sidebarRight/visualization/data/Data.tsx +++ b/app/src/components/layout/sidebarRight/visualization/data/Data.tsx @@ -131,7 +131,11 @@ const Data = () => { {/* Render groups dynamically */} { - chartDataGroups[selectedChartId?.id] && + chartDataGroups[selectedChartId?.id] && + <> +
2D Widget Input
+ + } {/* Info Box */} diff --git a/app/src/components/templates/CollaborationPopup.tsx b/app/src/components/templates/CollaborationPopup.tsx index 34e3669..8312fe7 100644 --- a/app/src/components/templates/CollaborationPopup.tsx +++ b/app/src/components/templates/CollaborationPopup.tsx @@ -50,7 +50,14 @@ const UserListTemplate: React.FC = ({ user }) => { ); }; -const CollaborationPopup: React.FC = () => { +interface CollaborateProps { + setUserManagement: (value: boolean) => void; +} + +const CollaborationPopup: React.FC = ({ + setUserManagement, +}) => { + const userName = localStorage.getItem("userName") || "Anonymous"; const users = [ { name: "Alice Johnson", @@ -81,16 +88,31 @@ const CollaborationPopup: React.FC = () => { access: "Viewer", }, ]; - return ( -
-
+
{ + setUserManagement(false); + }} + > +
{ + e.preventDefault(); + e.stopPropagation(); + }} + >
Share this file
copy link
-
+
{ + setUserManagement(false); + }} + >
@@ -111,7 +133,10 @@ const CollaborationPopup: React.FC = () => {
-
User 1
+
+
{userName[0]}
+ {userName} +
you
{users.map((user, index) => ( 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/ModuleToggle.tsx b/app/src/components/ui/ModuleToggle.tsx index e364f52..b7421af 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,18 +7,20 @@ 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 (
{ setActiveModule("builder"); - setToggleUI(true); + setToggleUI(localStorage.getItem('navBarUi') ? localStorage.getItem('navBarUi') === 'true' : true) }} >
@@ -30,7 +32,7 @@ const ModuleToggle: React.FC = () => { className={`module-list ${activeModule === "simulation" && "active"}`} onClick={() => { setActiveModule("simulation"); - setToggleUI(true); + setToggleUI(localStorage.getItem('navBarUi') ? localStorage.getItem('navBarUi') === 'true' : true) }} >
@@ -39,12 +41,11 @@ const ModuleToggle: React.FC = () => {
Simulation
{ setActiveModule("visualization"); - setToggleUI(true); + setToggleUI(localStorage.getItem('navBarUi') ? localStorage.getItem('navBarUi') === 'true' : true) }} >
diff --git a/app/src/components/ui/Tools.tsx b/app/src/components/ui/Tools.tsx index abe7ba8..e03484d 100644 --- a/app/src/components/ui/Tools.tsx +++ b/app/src/components/ui/Tools.tsx @@ -3,8 +3,10 @@ import { AsileIcon, CommentIcon, CursorIcon, + DeleteIcon, FloorIcon, FreeMoveIcon, + MeasureToolIcon, PenIcon, PlayIcon, SaveTemplateIcon, @@ -12,23 +14,30 @@ 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"; import { useSelectedZoneStore } from "../../store/useZoneStore"; import { + useActiveTool, useAddAction, useDeleteModels, + useDeletePointOrLine, + useMovePoint, + useRefTextUpdate, useSelectedWallItem, useToggleView, + useToolMode, + useTransformMode, } from "../../store/store"; +import useToggleStore from "../../store/useUIToggleStore"; const Tools: React.FC = () => { const { templates } = useTemplateStore(); - const [activeTool, setActiveTool] = useState("cursor"); const [activeSubTool, setActiveSubTool] = useState("cursor"); - const [toggleThreeD, setToggleThreeD] = useState(true); + const { toggleThreeD, setToggleThreeD } = useThreeDStore(); + const { setToggleUI } = useToggleStore(); const dropdownRef = useRef(null); const [openDrop, setOpenDrop] = useState(false); @@ -39,12 +48,27 @@ const Tools: React.FC = () => { const { selectedZone } = useSelectedZoneStore(); // wall options - const { setToggleView } = useToggleView(); + const { toggleView, setToggleView } = useToggleView(); const { setDeleteModels } = useDeleteModels(); const { setAddAction } = useAddAction(); const { setSelectedWallItem } = useSelectedWallItem(); + const { transformMode, setTransformMode } = useTransformMode(); + const { deletePointOrLine, setDeletePointOrLine } = useDeletePointOrLine(); + const { movePoint, setMovePoint } = useMovePoint(); + const { toolMode, setToolMode } = useToolMode(); + const { activeTool, setActiveTool } = useActiveTool(); + const { refTextupdate, setRefTextUpdate } = useRefTextUpdate(); + // Reset activeTool whenever activeModule changes + useEffect(() => { + setToggleUI( + localStorage.getItem("navBarUi") + ? localStorage.getItem("navBarUi") === "true" + : true + ); + }, []); + useEffect(() => { setActiveTool(activeSubTool); setActiveSubTool(activeSubTool); @@ -56,10 +80,18 @@ const Tools: React.FC = () => { setDeleteModels(false); setAddAction(null); setToggleView(true); + // localStorage.setItem("navBarUi", JSON.stringify(!toggleThreeD)); } else { setToggleView(false); } + setToggleUI( + localStorage.getItem("navBarUi") + ? localStorage.getItem("navBarUi") === "true" + : true + ); setToggleThreeD(!toggleThreeD); + setActiveSubTool("cursor"); + setActiveTool("cursor"); }; useEffect(() => { @@ -84,6 +116,90 @@ const Tools: React.FC = () => { document.removeEventListener("keydown", handleEscKeyPress); // Clean up the event listener }; }, []); + useEffect(() => { + if (!toggleThreeD) { + setToggleUI(false); + } + }, [toggleThreeD]); + + useEffect(() => { + setToolMode(null); + setDeleteModels(false); + setAddAction(null); + setTransformMode(null); + setMovePoint(false); + setDeletePointOrLine(false); + setRefTextUpdate((prevUpdate) => prevUpdate - 1); + + switch (activeTool) { + case "Move": + if (toggleView) { + setMovePoint(true); + } else { + setTransformMode("translate"); + } + break; + + case "Rotate": + if (!toggleView) { + setTransformMode("rotate"); + } + break; + + case "Scale": + if (!toggleView) { + setTransformMode("scale"); + } + break; + + case "draw-wall": + if (toggleView) { + setToolMode("Wall"); + } + break; + + case "draw-aisle": + if (toggleView) { + setToolMode("Aisle"); + } + break; + + case "draw-zone": + if (toggleView) { + setToolMode("Zone"); + } + break; + + case "draw-floor": + if (toggleView) { + setToolMode("Floor"); + } + break; + + case "measure": + setToolMode("MeasurementScale"); + break; + + case "Add pillar": + if (!toggleView) { + setAddAction("pillar"); + } + break; + + case "delete": + if (toggleView) { + setDeletePointOrLine(true); + } else { + setDeleteModels(true); + } + break; + + default: + break; + } + + setActiveTool(activeTool); + }, [activeTool]); return ( <> @@ -116,13 +232,24 @@ const Tools: React.FC = () => {
)} + {activeSubTool == "delete" && ( +
{ + setActiveTool("delete"); + }} + > + +
+ )} {activeModule !== "visualization" && (
{ setOpenDrop(!openDrop); - console.log(openDrop); }} > @@ -156,6 +283,20 @@ const Tools: React.FC = () => {
Free Hand
+
{ + setOpenDrop(false); + setActiveTool("delete"); + setActiveSubTool("delete"); + }} + > +
+ {activeSubTool === "delete" && } +
+ +
Delete
+
)}
@@ -173,6 +314,7 @@ const Tools: React.FC = () => { onClick={() => { setActiveTool("draw-wall"); }} + title="Wall" >
@@ -183,6 +325,7 @@ const Tools: React.FC = () => { onClick={() => { setActiveTool("draw-zone"); }} + title="Zone" >
@@ -193,6 +336,7 @@ const Tools: React.FC = () => { onClick={() => { setActiveTool("draw-aisle"); }} + title="Aisle" >
@@ -203,12 +347,31 @@ const Tools: React.FC = () => { onClick={() => { setActiveTool("draw-floor"); }} + title="Floor" >
)} + {activeModule === "builder" && ( + <> +
+
+
{ + setActiveTool("measure"); + }} + title="Measure" + > + +
+
+ + )} {activeModule === "simulation" && ( <>
@@ -257,31 +420,41 @@ const Tools: React.FC = () => { >
-
{ - setIsPlaying(!isPlaying); - }} - > - -
-
-
-
-
- 2d -
-
- 3d -
+ {toggleThreeD && ( +
{ + setIsPlaying(!isPlaying); + }} + > + +
+ )}
+ {activeModule === "builder" && ( + <> +
+
+
+ 2d +
+
+ 3d +
+
+ + )}
) : ( 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..7689e51 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,27 +148,47 @@ 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 (
{/* Left Arrow */} {showLeftArrow && ( @@ -185,7 +206,6 @@ const DisplayZone: React.FC = ({ 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 f79c55c..db8c408 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 @@ -296,18 +315,11 @@ export const DraggableWidget = ({ )} {widget.type === "polarArea" && ( )}
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..4e6d3fa 100644 --- a/app/src/components/ui/componets/DroppedFloatingWidgets.tsx +++ b/app/src/components/ui/componets/DroppedFloatingWidgets.tsx @@ -1,59 +1,88 @@ - - - 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"; +import TotalCardComponent from "../realTimeVis/floating/TotalCardComponent"; +import WarehouseThroughputComponent from "../realTimeVis/floating/WarehouseThroughputComponent"; +import FleetEfficiencyComponent from "../realTimeVis/floating/FleetEfficiencyComponent"; +import { useWidgetStore } from "../../../store/useWidgetStore"; 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 { setSelectedChartId } = useWidgetStore(); 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 + console.log("zone", 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 +90,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,59 +183,42 @@ 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)} + onClick={() => { + setSelectedChartId(obj) + }} > {obj.className === "floating total-card" ? ( -
-
-
{obj.header}
-
-
{obj.value}
-
{obj.per}
-
-
-
- -
-
+ <> + + ) : obj.className === "warehouseThroughput floating" ? ( -
-
-

Warehouse Throughput

-

- (+5) more in 2025 -

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

Fleet Efficiency

-
-
-
-
-
-
-
-
- 0% -
-
{obj.per}%
-
Optimal
-
- 100% -
-
+ <> + + ) : null}
))} @@ -156,59 +226,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..45db987 100644 --- a/app/src/components/ui/componets/RealTimeVisulization.tsx +++ b/app/src/components/ui/componets/RealTimeVisulization.tsx @@ -9,10 +9,15 @@ 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"; @@ -25,7 +30,7 @@ type FormattedZoneData = Record< lockedPanels: Side[]; zoneId: string; zoneViewPortTarget: number[]; - zoneViewPortPosition: number[] + zoneViewPortPosition: number[]; widgets: Widget[]; } >; @@ -45,9 +50,13 @@ 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(); + const { widgetSubOption, setWidgetSubOption } = useWidgetSubOption(); + useEffect(() => { async function GetZoneData() { const email = localStorage.getItem("email") || ""; @@ -57,27 +66,29 @@ 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 - + }, [activeModule]); // Removed `zones` from dependencies useEffect(() => { setZonesData((prev) => { @@ -97,48 +108,72 @@ const RealTimeVisulization: React.FC = () => { }; }); }, [selectedZone]); - - useEffect(() => { - }, [floatingWidgets]) + 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 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], - }, - })); + 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) {} }; - return (
{ width: isPlaying || activeModule !== "visualization" ? "100vw" : "", left: isPlaying || activeModule !== "visualization" ? "0%" : "", }} - >
{ onDrop={(event) => handleDrop(event)} onDragOver={(event) => event.preventDefault()} > -
diff --git a/app/src/components/ui/componets/functions/convertAutoToNumeric.ts b/app/src/components/ui/componets/functions/convertAutoToNumeric.ts new file mode 100644 index 0000000..3addac2 --- /dev/null +++ b/app/src/components/ui/componets/functions/convertAutoToNumeric.ts @@ -0,0 +1,41 @@ +import { getActiveProperties } from "./getActiveProperties"; + +export const convertAutoToNumeric = ( + canvasRect: DOMRect, + position: { + top: number | "auto"; + left: number | "auto"; + right: number | "auto"; + bottom: number | "auto"; + } +): { top: number; left: number; right: number; bottom: number } => { + 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/inputs/EyeDropInput.tsx b/app/src/components/ui/inputs/EyeDropInput.tsx index 2823392..72c5ded 100644 --- a/app/src/components/ui/inputs/EyeDropInput.tsx +++ b/app/src/components/ui/inputs/EyeDropInput.tsx @@ -1,18 +1,36 @@ import React from "react"; -import RegularDropDown from "./RegularDropDown"; import { EyeDroperIcon } from "../../icons/ExportCommonIcons"; +import RegularDropDown from "./RegularDropDown"; + +interface EyeDropInputProps { + label: string; + value: string; + onChange: (value: string) => void; + options?: string[]; +} + +const EyeDropInput: React.FC = ({ + label = "Object", + onChange, +}) => { + const handleEyeDropClick = () => { + // Here you would typically implement the eye dropper functionality + // For now, we'll just simulate selecting a value + const simulatedValue = "picked_value"; // Replace with actual eye dropper logic + onChange(simulatedValue); + }; -const EyeDropInput: React.FC = () => { return (
-
Object
+
{label}
+ {/* */} {}} + onSelect={() => { }} /> -
+
@@ -20,4 +38,4 @@ const EyeDropInput: React.FC = () => { ); }; -export default EyeDropInput; +export default EyeDropInput; \ No newline at end of file diff --git a/app/src/components/ui/inputs/MultiEmailInvite.tsx b/app/src/components/ui/inputs/MultiEmailInvite.tsx index 7cfa1a0..a16de66 100644 --- a/app/src/components/ui/inputs/MultiEmailInvite.tsx +++ b/app/src/components/ui/inputs/MultiEmailInvite.tsx @@ -58,12 +58,15 @@ const MultiEmailInvite: React.FC = () => { onFocus={() => setInputFocus(true)} onBlur={() => setInputFocus(false)} onKeyDown={handleKeyDown} - placeholder="Enter email and press Enter or comma" + placeholder="Enter email and press Enter or comma to seperate" />
Invite
+
+ {/* list available users */} +
); }; diff --git a/app/src/components/ui/inputs/MultiLevelDropDown.tsx b/app/src/components/ui/inputs/MultiLevelDropDown.tsx index 3385a5b..50f7fc8 100644 --- a/app/src/components/ui/inputs/MultiLevelDropDown.tsx +++ b/app/src/components/ui/inputs/MultiLevelDropDown.tsx @@ -253,7 +253,8 @@ const MultiLevelDropdown = ({ className={`dropdown-button ${open ? "open" : ""}`} onClick={() => setOpen(!open)} > - {displayLabel} +
{displayLabel}
+ {open && (
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/menu/menu.tsx b/app/src/components/ui/menu/menu.tsx new file mode 100644 index 0000000..b4ab78e --- /dev/null +++ b/app/src/components/ui/menu/menu.tsx @@ -0,0 +1,523 @@ +import React, { useState } from "react"; +import { ArrowIcon } from "../../icons/ExportCommonIcons"; + +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); + + // 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 ( +
{ + setOpenMenu(false); + }} + > + {/* Top-level menu buttons */} +
+ {/* File Menu */} +
setActiveMenu("File")} + onMouseLeave={() => { + setActiveMenu(null); + setActiveSubMenu(null); + }} + > +
+ File + + + +
+ + {/* File Dropdown */} + {activeMenu === "File" && ( +
+ {/* New File */} +
toggleSelection("New File")} + > +
+ New File +
+ Ctrl + N +
+
+
+ + {/* Open Local File */} +
toggleSelection("Open Local File")} + > +
+ Open Local File +
+ Ctrl + O +
+
+
+ + {/* Save Version */} +
toggleSelection("Save Version")} + > +
+ Save Version +
+
+
+ + {/* Make a Copy */} +
toggleSelection("Make a Copy")} + > +
+ Make a Copy +
+
+ + {/* Share */} +
toggleSelection("Share")} + > +
+ Share +
+
+ + {/* Rename */} +
toggleSelection("Rename")} + > +
+ Rename +
+
+
+ + {/* Import */} +
toggleSelection("Import")} + > +
+ Import +
+
+ + {/* Close File */} +
toggleSelection("Close File")} + > +
+ Close File +
+
+
+ )} +
+ + {/* Edit Menu */} +
setActiveMenu("Edit")} + onMouseLeave={() => { + setActiveMenu(null); + setActiveSubMenu(null); + }} + > +
+ Edit + + + +
+ + {/* Edit Dropdown */} + {activeMenu === "Edit" && ( +
+ {/* Undo */} +
toggleSelection("Undo")} + > +
+ Undo +
+ Ctrl + Z +
+
+
+ + {/* Redo */} +
toggleSelection("Redo")} + > +
+ Redo +
+ Ctrl + Shift + Z +
+
+
+
+ + {/* Undo History */} +
toggleSelection("Undo History")} + > +
+ Undo History +
+
+ + {/* Redo History */} +
toggleSelection("Redo History")} + > +
+ Redo History +
+
+
+ + {/* Find */} +
toggleSelection("Find")} + > +
+ Find +
+ Ctrl + F +
+
+
+ + {/* Delete */} +
toggleSelection("Delete")} + > +
+ Delete +
+
+ + {/* Select by... */} +
toggleSelection("Select by...")} + > +
+ Select by... +
+
+ + {/* Keymap */} +
toggleSelection("Keymap")} + > +
+ Keymap +
+
+
+ )} +
+ + {/* View Menu */} +
setActiveMenu("View")} + onMouseLeave={() => { + setActiveMenu(null); + setActiveSubMenu(null); + }} + > +
+ View + + + +
+ + {/* View Dropdown */} + {activeMenu === "View" && ( +
+ {/* Grid */} +
toggleSelection("Grid")} + > +
+ Grid +
+
+ + {/* Gizmo */} +
setActiveSubMenu("View-Gizmo")} + onMouseLeave={() => setActiveSubMenu(null)} + > +
+ Gizmo + + + +
+
+ + {/* Gizmo Submenu */} + {activeSubMenu === "View-Gizmo" && ( +
+ {/* Visibility */} +
toggleSelection("Visibility")} + > + + {selectedItems["Visibility"] && "✓ "} + Visibility + +
+
+ + {/* Cube view */} +
toggleSelection("Cube view")} + > + Cube view +
+ + {/* Sphere view */} +
toggleSelection("Sphere view")} + > + Sphere view +
+
+ )} +
+ + {/* Zoom */} +
toggleSelection("Zoom")} + > +
+ Zoom +
+
+ + {/* Full Screen */} +
toggleSelection("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 + + + +
+ + {activeMenu === "Apps" && ( +
+
toggleSelection("New App")} + > +
+ + + New App + +
+
+
+ +
toggleSelection("Work-flow Monitor")} + > +
+ + + Work-flow Monitor + +
+
+ +
toggleSelection("Temperature Visualizer")} + > +
+ + + Temperature Visualizer + +
+
+ +
toggleSelection("View all")} + > +
+ + + View all + +
+
+
+ )} +
*/} + + {/* Help Menu */} +
setActiveMenu("Help")} + onMouseLeave={() => { + setActiveMenu(null); + setActiveSubMenu(null); + }} + > +
+ Help + + + +
+ + {/* Help Dropdown */} + {activeMenu === "Help" && ( +
+ {/* Shortcuts */} +
toggleSelection("Shortcuts")} + > +
+ Shortcuts +
+ Ctrl + Shift + ? +
+
+
+ + {/* Manual */} +
toggleSelection("Manual")} + > +
+ Manual +
+
+ + {/* Video Tutorials */} +
toggleSelection("Video Tutorials")} + > +
+ Video Tutorials +
+
+ + {/* Report a bug */} +
toggleSelection("Report a bug")} + > +
+ Report a bug +
+
+
+ )} +
+
+
+ ); +}; + +export default MenuBar; diff --git a/app/src/components/ui/realTimeVis/charts/BarGraphComponent.tsx b/app/src/components/ui/realTimeVis/charts/BarGraphComponent.tsx index 8a18d0f..4876fe4 100644 --- a/app/src/components/ui/realTimeVis/charts/BarGraphComponent.tsx +++ b/app/src/components/ui/realTimeVis/charts/BarGraphComponent.tsx @@ -345,7 +345,7 @@ const BarGraphComponent = ({ if (id !== "") { try { - const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_LOCAL_BASE_URL}/api/v2/WidgetData/${id}/${organization}`); + const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/WidgetData/${id}/${organization}`); if (response.status === 200) { setmeasurements(response.data.Data.measurements) setDuration(response.data.Data.duration) diff --git a/app/src/components/ui/realTimeVis/charts/DoughnutGraphComponent.tsx b/app/src/components/ui/realTimeVis/charts/DoughnutGraphComponent.tsx index 8a6adf8..6eec49e 100644 --- a/app/src/components/ui/realTimeVis/charts/DoughnutGraphComponent.tsx +++ b/app/src/components/ui/realTimeVis/charts/DoughnutGraphComponent.tsx @@ -158,7 +158,7 @@ const DoughnutGraphComponent = ({ if (id !== "") { try { - const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_LOCAL_BASE_URL}/api/v2/WidgetData/${id}/${organization}`); + const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/WidgetData/${id}/${organization}`); if (response.status === 200) { setmeasurements(response.data.Data.measurements) setDuration(response.data.Data.duration) diff --git a/app/src/components/ui/realTimeVis/charts/LineGraphComponent.tsx b/app/src/components/ui/realTimeVis/charts/LineGraphComponent.tsx index 0425408..bf76add 100644 --- a/app/src/components/ui/realTimeVis/charts/LineGraphComponent.tsx +++ b/app/src/components/ui/realTimeVis/charts/LineGraphComponent.tsx @@ -158,7 +158,7 @@ const LineGraphComponent = ({ if (id !== "") { try { - const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_LOCAL_BASE_URL}/api/v2/WidgetData/${id}/${organization}`); + const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/WidgetData/${id}/${organization}`); if (response.status === 200) { setmeasurements(response.data.Data.measurements) setDuration(response.data.Data.duration) diff --git a/app/src/components/ui/realTimeVis/charts/PieGraphComponent.tsx b/app/src/components/ui/realTimeVis/charts/PieGraphComponent.tsx index 8f32791..094b9e7 100644 --- a/app/src/components/ui/realTimeVis/charts/PieGraphComponent.tsx +++ b/app/src/components/ui/realTimeVis/charts/PieGraphComponent.tsx @@ -344,7 +344,7 @@ const PieChartComponent = ({ if (id !== "") { try { - const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_LOCAL_BASE_URL}/api/v2/WidgetData/${id}/${organization}`); + const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/WidgetData/${id}/${organization}`); if (response.status === 200) { setmeasurements(response.data.Data.measurements) setDuration(response.data.Data.duration) diff --git a/app/src/components/ui/realTimeVis/charts/PolarAreaGraphComponent.tsx b/app/src/components/ui/realTimeVis/charts/PolarAreaGraphComponent.tsx index 563e1e9..92581c0 100644 --- a/app/src/components/ui/realTimeVis/charts/PolarAreaGraphComponent.tsx +++ b/app/src/components/ui/realTimeVis/charts/PolarAreaGraphComponent.tsx @@ -1,22 +1,64 @@ -import { useMemo } from "react"; -import { Line, PolarArea } from "react-chartjs-2"; +import React, { useEffect, useMemo, useState } from "react"; +import { PolarArea } from "react-chartjs-2"; +import io from "socket.io-client"; +import { useThemeStore } from "../../../../store/useThemeStore"; +import useChartStore from "../../../../store/useChartStore"; +import { useWidgetStore } from "../../../../store/useWidgetStore"; +import axios from "axios"; interface ChartComponentProps { + id: string; type: any; title: string; fontFamily?: string; fontSize?: string; fontWeight?: "Light" | "Regular" | "Bold"; - data: any; } const PolarAreaGraphComponent = ({ + id, + type, title, fontFamily, fontSize, fontWeight = "Regular", }: ChartComponentProps) => { - // Memoize Font Weight Mapping + const { themeColor } = useThemeStore(); + const { measurements: chartMeasurements, duration: chartDuration, name: widgetName } = useChartStore(); + const [measurements, setmeasurements] = useState({}); + const [duration, setDuration] = useState("1h") + const [name, setName] = useState("Widget") + const [chartData, setChartData] = useState<{ labels: string[]; datasets: any[] }>({ + labels: [], + datasets: [], + }); + const { selectedChartId } = useWidgetStore(); + + const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL; + const email = localStorage.getItem("email") || ""; + const organization = email?.split("@")[1]?.split(".")[0] + const defaultData = { + labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"], + datasets: [ + { + label: "Dataset", + data: [12, 19, 3, 5, 2, 3], + backgroundColor: ["#6f42c1"], + borderColor: "#b392f0", + borderWidth: 1, + }, + ], + }; + + useEffect(() => { + console.log("titleeeeeeeeeeeeeeeeeee",title); + },[]) + + // Memoize Theme Colors + const buttonActionColor = useMemo(() => themeColor[0] || "#5c87df", [themeColor]); + const buttonAbortColor = useMemo(() => themeColor[1] || "#ffffff", [themeColor]); + + // Memoize Font Styling const chartFontWeightMap = useMemo( () => ({ Light: "lighter" as const, @@ -26,19 +68,9 @@ const PolarAreaGraphComponent = ({ [] ); - // Parse and Memoize Font Size - const fontSizeValue = useMemo( - () => (fontSize ? parseInt(fontSize) : 12), - [fontSize] - ); + const fontSizeValue = useMemo(() => (fontSize ? parseInt(fontSize) : 12), [fontSize]); + const fontWeightValue = useMemo(() => chartFontWeightMap[fontWeight], [fontWeight, chartFontWeightMap]); - // Determine and Memoize Font Weight - const fontWeightValue = useMemo( - () => chartFontWeightMap[fontWeight], - [fontWeight, chartFontWeightMap] - ); - - // Memoize Chart Font Style const chartFontStyle = useMemo( () => ({ family: fontFamily || "Arial", @@ -48,46 +80,110 @@ const PolarAreaGraphComponent = ({ [fontFamily, fontSizeValue, fontWeightValue] ); - const options = useMemo( - () => ({ - responsive: true, - maintainAspectRatio: false, - plugins: { - title: { - display: true, - text: title, - font: chartFontStyle, - }, - legend: { - display: false, - }, - }, - scales: { - x: { - ticks: { - display: false, // This hides the x-axis labels + // Memoize Chart Options + const options = useMemo( + () => ({ + responsive: true, + maintainAspectRatio: false, + plugins: { + title: { + display: true, + text: name, + font: chartFontStyle, + }, + legend: { + display: false, }, }, - }, - }), - [title, chartFontStyle] - ); + scales: { + // x: { + // ticks: { + // display: true, // This hides the x-axis labels + // }, + // }, + }, + }), + [title, chartFontStyle, name] + ); - const chartData = { - labels: ["January", "February", "March", "April", "May", "June", "July"], - datasets: [ - { - label: "My First Dataset", - data: [65, 59, 80, 81, 56, 55, 40], - backgroundColor: "#6f42c1", // Updated to #6f42c1 (Purple) - borderColor: "#ffffff", // Keeping border color white - borderWidth: 2, - fill: false, - }, - ], - }; + // useEffect(() => {console.log(measurements); + // },[measurements]) - return ; + useEffect(() => { + if (!iotApiUrl || !measurements || Object.keys(measurements).length === 0) return; + + const socket = io(`http://${iotApiUrl}`); + + const inputData = { + measurements, + duration, + interval: 1000, + }; + + + const startStream = () => { + socket.emit("lineInput", inputData); + }; + + socket.on("connect", startStream); + + socket.on("lineOutput", (response) => { + const responseData = response.data; + + // Extract timestamps and values + const labels = responseData.time; + const datasets = Object.keys(measurements).map((key) => { + const measurement = measurements[key]; + const datasetKey = `${measurement.name}.${measurement.fields}`; + return { + label: datasetKey, + data: responseData[datasetKey]?.values ?? [], + backgroundColor: "#6f42c1", + borderColor: "#b392f0", + borderWidth: 1, + }; + }); + + setChartData({ labels, datasets }); + }); + + return () => { + socket.off("lineOutput"); + socket.emit("stop_stream"); // Stop streaming when component unmounts + socket.disconnect(); + }; + }, [measurements, duration, iotApiUrl]); + + const fetchSavedInputes = async() => { + + if (id !== "") { + try { + const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/WidgetData/${id}/${organization}`); + if (response.status === 200) { + setmeasurements(response.data.Data.measurements) + setDuration(response.data.Data.duration) + setName(response.data.widgetName) + } else { + console.log("Unexpected response:", response); + } + } catch (error) { + console.error("There was an error!", error); + } + } + } + + useEffect(() => { + fetchSavedInputes(); + }, []); + + useEffect(() => { + if (selectedChartId?.id === id) { + fetchSavedInputes(); + } + } + ,[chartMeasurements, chartDuration, widgetName]) + + return 0 ? chartData : defaultData} options={options} />; }; -export default PolarAreaGraphComponent; +export default PolarAreaGraphComponent; \ No newline at end of file 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/FleetEfficiencyComponent.tsx b/app/src/components/ui/realTimeVis/floating/FleetEfficiencyComponent.tsx new file mode 100644 index 0000000..9b2fd7f --- /dev/null +++ b/app/src/components/ui/realTimeVis/floating/FleetEfficiencyComponent.tsx @@ -0,0 +1,33 @@ +import React from 'react' + +type Props = {} + +const FleetEfficiencyComponent = ({ + object +}: any) => { + return ( + <> +

Fleet Efficiency

+
+
+
+
+
+
+
+
+ 0% +
+
{object.per}%
+
Optimal
+
+ 100% +
+ + ) +} + +export default FleetEfficiencyComponent \ No newline at end of file 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/components/ui/realTimeVis/floating/TotalCardComponent.tsx b/app/src/components/ui/realTimeVis/floating/TotalCardComponent.tsx new file mode 100644 index 0000000..00ec479 --- /dev/null +++ b/app/src/components/ui/realTimeVis/floating/TotalCardComponent.tsx @@ -0,0 +1,29 @@ +import React from 'react' +import { WalletIcon } from '../../../icons/3dChartIcons' +import { useWidgetStore } from '../../../../store/useWidgetStore'; + +const TotalCardComponent = ({ + object +}: any) => { + + const { setSelectedChartId } = + useWidgetStore(); + return ( + <> +
{ + setSelectedChartId(object.id) + }}> +
{object.header}
+
+
{object.value}
+
{object.per}
+
+
+
+ +
+ + ) +} + +export default TotalCardComponent \ No newline at end of file diff --git a/app/src/components/ui/realTimeVis/floating/WarehouseThroughputComponent.tsx b/app/src/components/ui/realTimeVis/floating/WarehouseThroughputComponent.tsx new file mode 100644 index 0000000..3aa8698 --- /dev/null +++ b/app/src/components/ui/realTimeVis/floating/WarehouseThroughputComponent.tsx @@ -0,0 +1,132 @@ +import React, { useState } from 'react' +import { Line } from 'react-chartjs-2' +import axios from 'axios'; + +const WarehouseThroughputComponent = ({ + object +}: any) => { + + const [measurements, setmeasurements] = useState({}); + const [duration, setDuration] = useState("1h") + + const lineGraphData = { + labels: [ + "Jan", + "Feb", + "Mar", + "Apr", + "May", + "Jun", + "Jul", + "Aug", + "Sep", + "Oct", + "Nov", + "Dec", + ], // Months of the year + datasets: [ + { + label: "Throughput (units/month)", + data: [500, 400, 300, 450, 350, 250, 200, 300, 250, 150, 100, 150], // Example monthly data + borderColor: "#6f42c1", // Use the desired color for the line (purple) + backgroundColor: "rgba(111, 66, 193, 0.2)", // Use a semi-transparent purple for the fill + borderWidth: 2, // Line thickness + fill: true, // Enable fill for this dataset + pointRadius: 0, // Remove dots at each data point + tension: 0.5, // Smooth interpolation for the line + }, + ], + }; + + // Line graph options + const lineGraphOptions = { + responsive: true, + maintainAspectRatio: false, // Allow custom height/width adjustments + plugins: { + legend: { + display: false, // Hide legend + }, + title: { + display: false, // No chart title needed + }, + tooltip: { + callbacks: { + label: (context: any) => { + const value = context.parsed.y; + return `${value} units`; // Customize tooltip to display "units" + }, + }, + }, + }, + scales: { + x: { + grid: { + display: false, // Hide x-axis grid lines + }, + ticks: { + maxRotation: 0, // Prevent label rotation + autoSkip: false, // Display all months + font: { + size: 8, // Adjust font size for readability + color: "#ffffff", // Light text color for labels + }, + }, + }, + y: { + display: true, // Show y-axis + grid: { + drawBorder: false, // Remove border line + color: "rgba(255, 255, 255, 0.2)", // Light gray color for grid lines + borderDash: [5, 5], // Dotted line style (array defines dash and gap lengths) + }, + ticks: { + font: { + size: 8, // Adjust font size for readability + color: "#ffffff", // Light text color for ticks + }, + }, + }, + }, + elements: { + line: { + tension: 0.5, // Smooth interpolation for the line + }, + }, + }; + + + // const fetchSavedInputes = async() => { + + // if (object.id !== "") { + // try { + // const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_LOCAL_BASE_URL}/api/v2/WidgetData/${id}/${organization}`); + // if (response.status === 200) { + // setmeasurements(response.data.Data.measurements) + // setDuration(response.data.Data.duration) + // setName(response.data.widgetName) + // } else { + // console.log("Unexpected response:", response); + // } + // } catch (error) { + // console.error("There was an error!", error); + // } + // } + // } + + + return ( + <> +
+

Warehouse Throughput

+

+ (+5) more in 2025 +

+
+
+ +
+ + ) +} + +export default WarehouseThroughputComponent \ No newline at end of file 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/functions/draw.ts b/app/src/modules/builder/functions/draw.ts index fc4dafa..0172707 100644 --- a/app/src/modules/builder/functions/draw.ts +++ b/app/src/modules/builder/functions/draw.ts @@ -17,7 +17,7 @@ async function Draw( floorPlanGroup: Types.RefGroup, ReferenceLineMesh: Types.RefMesh, LineCreated: Types.RefBoolean, - setRefTextUpdate: Types.NumberIncrementState, + setRefTextUpdate: any, Tube: Types.RefTubeGeometry, anglesnappedPoint: Types.RefVector3, isAngleSnapped: Types.RefBoolean, 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/groups/floorItemsGroup.tsx b/app/src/modules/builder/groups/floorItemsGroup.tsx index 8ddcdc9..cf9d6b8 100644 --- a/app/src/modules/builder/groups/floorItemsGroup.tsx +++ b/app/src/modules/builder/groups/floorItemsGroup.tsx @@ -193,7 +193,7 @@ const FloorItemsGroup = ({ itemsGroup, hoveredDeletableFloorItem, AttachedObject } const Mode = transformMode; - if (Mode !== null || activeTool === "Cursor") { + if (Mode !== null || activeTool === "cursor") { if (!itemsGroup.current) return; let intersects = raycaster.intersectObjects(itemsGroup.current.children, true); if (intersects.length > 0 && intersects[0]?.object?.parent?.parent?.position && intersects[0]?.object?.parent?.parent?.scale && intersects[0]?.object?.parent?.parent?.rotation) { @@ -225,7 +225,7 @@ const FloorItemsGroup = ({ itemsGroup, hoveredDeletableFloorItem, AttachedObject const Mode = transformMode; - if (Mode !== null || activeTool === "Cursor") { + if (Mode !== null || activeTool === "cursor") { if (!itemsGroup.current) return; let intersects = raycaster.intersectObjects(itemsGroup.current.children, true); if (intersects.length > 0 && intersects[0]?.object?.parent?.parent?.position && intersects[0]?.object?.parent?.parent?.scale && intersects[0]?.object?.parent?.parent?.rotation) { diff --git a/app/src/modules/collaboration/collabCams.tsx b/app/src/modules/collaboration/collabCams.tsx index 9d5475e..5ebe1eb 100644 --- a/app/src/modules/collaboration/collabCams.tsx +++ b/app/src/modules/collaboration/collabCams.tsx @@ -1,21 +1,20 @@ -import * as THREE from 'three'; -import { useEffect, useRef, useState } from 'react'; -import { useFrame } from '@react-three/fiber'; -import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'; -import camModel from '../../assets/gltf-glb/camera face 2.gltf'; -import getActiveUsersData from '../../services/factoryBuilder/collab/getActiveUsers'; -import { useActiveUsers, useSocketStore } from '../../store/store'; -import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader'; -import { useNavigate } from 'react-router-dom'; -import { Text, Html } from '@react-three/drei'; -import CollabUserIcon from './collabUserIcon'; -import image from '../../assets/image/userImage.png'; - +import * as THREE from "three"; +import { useEffect, useRef, useState } from "react"; +import { useFrame } from "@react-three/fiber"; +import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader"; +import camModel from "../../assets/gltf-glb/camera face 2.gltf"; +import getActiveUsersData from "../../services/factoryBuilder/collab/getActiveUsers"; +import { useActiveUsers, useSocketStore } from "../../store/store"; +import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader"; +import { useNavigate } from "react-router-dom"; +import { Html } from "@react-three/drei"; +import CollabUserIcon from "./collabUserIcon"; +import { getAvatarColor } from "./users/functions/getAvatarColor"; const CamModelsGroup = () => { let navigate = useNavigate(); const groupRef = useRef(null); - const email = localStorage.getItem('email'); + const email = localStorage.getItem("email"); const { activeUsers, setActiveUsers } = useActiveUsers(); const { socket } = useSocketStore(); const loader = new GLTFLoader(); @@ -23,93 +22,147 @@ const CamModelsGroup = () => { const [cams, setCams] = useState([]); const [models, setModels] = useState>({}); - dracoLoader.setDecoderPath('three/examples/jsm/libs/draco/gltf/'); + dracoLoader.setDecoderPath("three/examples/jsm/libs/draco/gltf/"); loader.setDRACOLoader(dracoLoader); useEffect(() => { if (!email) { - navigate('/'); + navigate("/"); } if (!socket) return; - const organization = email!.split('@')[1].split('.')[0]; + const organization = email!.split("@")[1].split(".")[0]; - socket.on('userConnectRespones', (data: any) => { + socket.on("userConnectRespones", (data: any) => { if (!groupRef.current) return; - if (data.data.userData.email === email) return - if (socket.id === data.socketId || organization !== data.organization) return; + if (data.data.userData.email === email) return; + if (socket.id === data.socketId || organization !== data.organization) + return; - const model = groupRef.current.getObjectByProperty('uuid', data.data.userData._id); + const model = groupRef.current.getObjectByProperty( + "uuid", + data.data.userData._id + ); if (model) { groupRef.current.remove(model); } loader.load(camModel, (gltf) => { const newModel = gltf.scene.clone(); newModel.uuid = data.data.userData._id; - newModel.position.set(data.data.position.x, data.data.position.y, data.data.position.z); - newModel.rotation.set(data.data.rotation.x, data.data.rotation.y, data.data.rotation.z); + newModel.position.set( + data.data.position.x, + data.data.position.y, + data.data.position.z + ); + newModel.rotation.set( + data.data.rotation.x, + data.data.rotation.y, + data.data.rotation.z + ); newModel.userData = data.data.userData; setCams((prev) => [...prev, newModel]); setActiveUsers([...activeUsers, data.data.userData]); }); }); - socket.on('userDisConnectRespones', (data: any) => { + socket.on("userDisConnectRespones", (data: any) => { if (!groupRef.current) return; - if (socket.id === data.socketId || organization !== data.organization) return; + if (socket.id === data.socketId || organization !== data.organization) + return; - setCams((prev) => prev.filter((cam) => cam.uuid !== data.data.userData._id)); - setActiveUsers(activeUsers.filter((user: any) => user._id !== data.data.userData._id)); + setCams((prev) => + prev.filter((cam) => cam.uuid !== data.data.userData._id) + ); + setActiveUsers( + activeUsers.filter((user: any) => user._id !== data.data.userData._id) + ); }); - socket.on('cameraUpdateResponse', (data: any) => { - if (!groupRef.current || socket.id === data.socketId || organization !== data.organization) return; + socket.on("cameraUpdateResponse", (data: any) => { + if ( + !groupRef.current || + socket.id === data.socketId || + organization !== data.organization + ) + return; setModels((prev) => ({ ...prev, [data.data.userId]: { - targetPosition: new THREE.Vector3(data.data.position.x, data.data.position.y, data.data.position.z), - targetRotation: new THREE.Euler(data.data.rotation.x, data.data.rotation.y, data.data.rotation.z), + targetPosition: new THREE.Vector3( + data.data.position.x, + data.data.position.y, + data.data.position.z + ), + targetRotation: new THREE.Euler( + data.data.rotation.x, + data.data.rotation.y, + data.data.rotation.z + ), }, })); }); return () => { - socket.off('userConnectRespones'); - socket.off('userDisConnectRespones'); - socket.off('cameraUpdateResponse'); + socket.off("userConnectRespones"); + socket.off("userDisConnectRespones"); + socket.off("cameraUpdateResponse"); }; }, [socket, activeUsers]); useFrame(() => { if (!groupRef.current) return; Object.keys(models).forEach((uuid) => { - const model = groupRef.current!.getObjectByProperty('uuid', uuid); + const model = groupRef.current!.getObjectByProperty("uuid", uuid); if (!model) return; const { targetPosition, targetRotation } = models[uuid]; model.position.lerp(targetPosition, 0.1); - model.rotation.x = THREE.MathUtils.lerp(model.rotation.x, targetRotation.x, 0.1); - model.rotation.y = THREE.MathUtils.lerp(model.rotation.y, targetRotation.y, 0.1); - model.rotation.z = THREE.MathUtils.lerp(model.rotation.z, targetRotation.z, 0.1); + model.rotation.x = THREE.MathUtils.lerp( + model.rotation.x, + targetRotation.x, + 0.1 + ); + model.rotation.y = THREE.MathUtils.lerp( + model.rotation.y, + targetRotation.y, + 0.1 + ); + model.rotation.z = THREE.MathUtils.lerp( + model.rotation.z, + targetRotation.z, + 0.1 + ); }); }); useEffect(() => { if (!groupRef.current) return; - const organization = email!.split('@')[1].split('.')[0]; + const organization = email!.split("@")[1].split(".")[0]; getActiveUsersData(organization).then((data) => { - const filteredData = data.cameraDatas.filter((camera: any) => camera.userData.email !== email); + 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) => { const newModel = gltf.scene.clone(); newModel.uuid = cam.userData._id; - newModel.position.set(cam.position.x, cam.position.y, cam.position.z); - newModel.rotation.set(cam.rotation.x, cam.rotation.y, cam.rotation.z); + newModel.position.set( + cam.position.x, + cam.position.y, + cam.position.z + ); + newModel.rotation.set( + cam.rotation.x, + cam.rotation.y, + cam.rotation.z + ); newModel.userData = cam.userData; - setActiveUsers([...activeUsers, cam.userData]); + a.push(cam.userData); return newModel; }); + setActiveUsers(a); setCams((prev) => [...prev, ...newCams]); }); } @@ -119,7 +172,7 @@ const CamModelsGroup = () => { return ( {cams.map((cam, index) => ( - + { textAlign: "center", fontFamily: "Arial, sans-serif", }} - position={[-0.015, 0, 0.7]}> - + position={[-0.015, 0, 0.7]} + > + ))} diff --git a/app/src/modules/collaboration/collabUserIcon.tsx b/app/src/modules/collaboration/collabUserIcon.tsx index a6c22c0..acc3fa0 100644 --- a/app/src/modules/collaboration/collabUserIcon.tsx +++ b/app/src/modules/collaboration/collabUserIcon.tsx @@ -1,53 +1,33 @@ import React from "react"; +import CustomAvatar from "./users/Avatar"; interface CollabUserIconProps { - color: string; - userImage: string; - userName: string; + userName: string; + userImage?: string; + index?: number; + color: string; } const CollabUserIcon: React.FC = ({ - color, - userImage, - userName, + userImage, + userName, + index = 0, + color, }) => { - return ( -
- {userName} -
- {userName} -
-
- ); + return ( +
+
+ {userImage ? ( + {userName} + ) : ( + + )} +
+
+ {userName} +
+
+ ); }; export default CollabUserIcon; diff --git a/app/src/modules/collaboration/users/Avatar.tsx b/app/src/modules/collaboration/users/Avatar.tsx new file mode 100644 index 0000000..f08a545 --- /dev/null +++ b/app/src/modules/collaboration/users/Avatar.tsx @@ -0,0 +1,59 @@ +import React, { useEffect, useState } from "react"; +import { getInitials } from "./functions/getInitials"; +import { getAvatarColor } from "./functions/getAvatarColor"; + +interface AvatarProps { + name: string; // Name can be a full name or initials + size?: number; + index?: number; + textColor?: string; +} + +const CustomAvatar: React.FC = ({ + name, + size = 100, + index = 0, + textColor = "#ffffff", +}) => { + const [imageSrc, setImageSrc] = useState(null); + + useEffect(() => { + const canvas = document.createElement("canvas"); // Create an offscreen canvas + canvas.width = size; + canvas.height = size; + const ctx = canvas.getContext("2d"); + if (ctx) { + const initials = getInitials(name); // Convert name to initials if needed + + // Draw background + ctx.fillStyle = getAvatarColor(index); + ctx.fillRect(0, 0, size, size); + + // Draw initials + ctx.fillStyle = textColor; + ctx.font = `bold ${size / 2}px Arial`; + ctx.textAlign = "center"; + ctx.textBaseline = "middle"; + ctx.fillText(initials, size / 2, size / 2); + + // Generate image source + const dataURL = canvas.toDataURL("image/png"); + setImageSrc(dataURL); + } + }, [name, size, textColor]); + + if (!imageSrc) { + return null; // Return null while the image is being generated + } + + return ( + User Avatar + ); +}; + +export default CustomAvatar; diff --git a/app/src/modules/collaboration/users/functions/getAvatarColor.ts b/app/src/modules/collaboration/users/functions/getAvatarColor.ts new file mode 100644 index 0000000..3deacca --- /dev/null +++ b/app/src/modules/collaboration/users/functions/getAvatarColor.ts @@ -0,0 +1,26 @@ +const avatarColors: string[] = [ + "#FF5733", // Red Orange + "#48ac2a", // Leaf Green + "#0050eb", // Royal Blue + "#FF33A1", // Hot Pink + "#FF8C33", // Deep Orange + "#8C33FF", // Violet + "#FF3333", // Bright Red + "#43c06d", // Emerald Green + "#A133FF", // Amethyst Purple + "#C70039", // Crimson + "#900C3F", // Maroon + "#581845", // Plum + "#3498DB", // Sky Blue + "#2ECC71", // Green Mint + "#E74C3C", // Tomato Red + "#00adff", // Azure + "#DBAD05", // Amber Yellow + "#FF5733", // Red Orange + "#FF33A1", // Hot Pink + "#900C3F", // Maroon +]; + +export function getAvatarColor(index: number): string { + return avatarColors[index % avatarColors.length]; +} diff --git a/app/src/modules/collaboration/users/functions/getInitials.ts b/app/src/modules/collaboration/users/functions/getInitials.ts new file mode 100644 index 0000000..572d49a --- /dev/null +++ b/app/src/modules/collaboration/users/functions/getInitials.ts @@ -0,0 +1,10 @@ +export const getInitials = (fullName: string): string => { + // Extract initials from the name + const words = fullName.split(" "); + const initials = words + .map((word) => word[0]) + .slice(0, 2) + .join("") + .toUpperCase(); + return initials; +}; \ No newline at end of file diff --git a/app/src/modules/market/AssetPreview.tsx b/app/src/modules/market/AssetPreview.tsx index 9a65990..854ebc4 100644 --- a/app/src/modules/market/AssetPreview.tsx +++ b/app/src/modules/market/AssetPreview.tsx @@ -9,10 +9,11 @@ import * as THREE from "three"; // Define the shape of the selected card interface SelectedCard { assetName: string; - uploadedOn: string; + uploadedOn: number; price: number; rating: number; views: number; + description: string; } // Define the props type for AssetPreview @@ -24,7 +25,7 @@ interface AssetPreviewProps { function Ui() { return ( - Loading your model... + Loading preview... ); } @@ -93,19 +94,7 @@ const AssetPreview: React.FC = ({
{selectedCard.assetName}
- Lorem ipsum dolor sit amet consectetur adipisicing elit. - Doloremque nisi beatae facilis architecto quaerat delectus velit - aliquid assumenda cumque vitae! Tempore quibusdam ab natus in - minima voluptates, aliquid corrupti excepturi consectetur - distinctio sequi beatae odit autem? Distinctio ab, voluptatem - omnis quibusdam, incidunt eum ipsa aliquid enim eaque eveniet nisi - autem, accusantium vel! Laborum in iste voluptates ad! Harum eum - amet pariatur fugit laudantium dolorem maxime voluptates atque - molestiae modi inventore quidem maiores dolore numquam, natus - quisquam optio distinctio eveniet aliquam, aut eligendi laboriosam - eaque! Porro cumque cum distinctio ullam debitis, dolorum - similique! Harum cupiditate perferendis voluptatum molestiae, - fugiat quisquam assumenda! + {`${selectedCard.assetName} is used in factories to improve efficiency and production speed It is designed to handle heavy workloads and perform repetitive tasks with precision. Many industries rely on this machine to manufacture products quickly and accurately. It reduces human effort and minimizes errors in the production process. Regular maintenance is required to keep the machine in good working condition.With advanced technology, this machine continues to enhance industrial operations and increase productivity.`}
diff --git a/app/src/modules/market/Card.tsx b/app/src/modules/market/Card.tsx index e723f4f..ec9db06 100644 --- a/app/src/modules/market/Card.tsx +++ b/app/src/modules/market/Card.tsx @@ -12,17 +12,19 @@ import { getAssetDownload } from "../../services/marketplace/getAssetDownload"; interface CardProps { assetName: string; - uploadedOn: string; + uploadedOn: number; price: number; rating: number; views: number; image: string; + description: string; onSelectCard: (cardData: { assetName: string; - uploadedOn: string; + uploadedOn: number; price: number; rating: number; views: number; + description: string; }) => void; } @@ -33,10 +35,11 @@ const Card: React.FC = ({ rating, views, image, + description, onSelectCard, }) => { const handleCardSelect = () => { - onSelectCard({ assetName, uploadedOn, price, rating, views }); + onSelectCard({ assetName, uploadedOn, price, rating, views, description }); }; return ( diff --git a/app/src/modules/market/CardsContainer.tsx b/app/src/modules/market/CardsContainer.tsx index 423fd16..c2a7e6d 100644 --- a/app/src/modules/market/CardsContainer.tsx +++ b/app/src/modules/market/CardsContainer.tsx @@ -1,8 +1,7 @@ import React, { useEffect, useState } from "react"; import Card from "./Card"; import AssetPreview from "./AssetPreview"; -import RenderOverlay from "../../components/templates/Overlay"; -import { fetchAssets } from "../../services/marketplace/fetchAssets"; + interface ModelData { CreatedBy: string; animated: string | null; @@ -23,18 +22,20 @@ interface ModelsProps { const CardsContainer: React.FC = ({ models }) => { const [selectedCard, setSelectedCard] = useState<{ assetName: string; - uploadedOn: string; + uploadedOn: number; price: number; rating: number; views: number; + description: string; } | null>(null); const handleCardSelect = (cardData: { assetName: string; - uploadedOn: string; + uploadedOn: number; price: number; rating: number; views: number; + description: string; }) => { setSelectedCard(cardData); }; @@ -48,12 +49,13 @@ const CardsContainer: React.FC = ({ models }) => { ))} {/* */} diff --git a/app/src/modules/market/FilterSearch.tsx b/app/src/modules/market/FilterSearch.tsx index 08103ba..84074b8 100644 --- a/app/src/modules/market/FilterSearch.tsx +++ b/app/src/modules/market/FilterSearch.tsx @@ -29,11 +29,10 @@ const FilterSearch: React.FC = ({ filteredModels, }) => { const [activeOption, setActiveOption] = useState("Sort by"); // State for active option - console.log("filteredModels: ", filteredModels); const handleSelect = (option: string) => { setActiveOption(option); - console.log("option: ", option); + // Alphabet ascending // Alphabet descending // All 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/modules/scene/tools/measurementTool.tsx b/app/src/modules/scene/tools/measurementTool.tsx index 3c56a21..f8054af 100644 --- a/app/src/modules/scene/tools/measurementTool.tsx +++ b/app/src/modules/scene/tools/measurementTool.tsx @@ -36,7 +36,7 @@ const MeasurementTool = () => { isLeftMouseDown = false; if (evt.button === 0 && !drag) { raycaster.setFromCamera(pointer, camera); - const intersects = raycaster.intersectObjects(scene.children, true).filter(intersect => !intersect.object.name.includes("Roof") && !intersect.object.name.includes("MeasurementReference") && !(intersect.object.type === "GridHelper")); + const intersects = raycaster.intersectObjects(scene.children, true).filter(intersect => !intersect.object.name.includes("Roof") && !intersect.object.name.includes("MeasurementReference") && !intersect.object.name.includes("agv-collider") && !(intersect.object.type === "GridHelper")); if (intersects.length > 0) { const intersectionPoint = intersects[0].point.clone(); @@ -83,7 +83,7 @@ const MeasurementTool = () => { useFrame(() => { if (points.length === 1) { raycaster.setFromCamera(pointer, camera); - const intersects = raycaster.intersectObjects(scene.children, true).filter(intersect => !intersect.object.name.includes("Roof") && !intersect.object.name.includes("MeasurementReference") && !(intersect.object.type === "GridHelper")); + const intersects = raycaster.intersectObjects(scene.children, true).filter(intersect => !intersect.object.name.includes("Roof") && !intersect.object.name.includes("MeasurementReference") && !intersect.object.name.includes("agv-collider") && !(intersect.object.type === "GridHelper")); if (intersects.length > 0) { updateMeasurement(points[0], intersects[0].point); diff --git a/app/src/modules/scene/world/world.tsx b/app/src/modules/scene/world/world.tsx index 16b4b53..eb7d555 100644 --- a/app/src/modules/scene/world/world.tsx +++ b/app/src/modules/scene/world/world.tsx @@ -29,6 +29,7 @@ import { useUpdateScene, useWalls, useToolMode, + useRefTextUpdate, } from "../../../store/store"; ////////// 3D Function Imports ////////// @@ -118,7 +119,7 @@ export default function World() { const { shadows, setShadows } = useShadows(); const { updateScene, setUpdateScene } = useUpdateScene(); const { walls, setWalls } = useWalls(); - const [RefTextupdate, setRefTextUpdate] = useState(-1000); + const { refTextupdate, setRefTextUpdate } = useRefTextUpdate(); // const loader = new GLTFLoader(); // const dracoLoader = new DRACOLoader(); @@ -158,7 +159,7 @@ export default function World() { ////////// All Toggle's ////////// useEffect(() => { - setRefTextUpdate((prevUpdate) => prevUpdate - 1); + setRefTextUpdate((prevUpdate: number) => prevUpdate - 1); if (dragPointControls.current) { dragPointControls.current.enabled = false; } @@ -241,7 +242,7 @@ export default function World() { diff --git a/app/src/modules/simulation/behaviour/behaviour.tsx b/app/src/modules/simulation/behaviour/behaviour.tsx index 2854172..c1281b5 100644 --- a/app/src/modules/simulation/behaviour/behaviour.tsx +++ b/app/src/modules/simulation/behaviour/behaviour.tsx @@ -52,7 +52,7 @@ function Behaviour() { ], assetPosition: [...item.position], assetRotation: [item.rotation.x, item.rotation.y, item.rotation.z], - speed: 1, + speed: 'Inherit', }; newPaths.push(newPath); @@ -67,12 +67,11 @@ function Behaviour() { point: { uuid: pointUUID, position: [pointPosition.x, pointPosition.y, pointPosition.z], - actions: [{ uuid: THREE.MathUtils.generateUUID(), name: 'Action 1', type: 'Start', start: THREE.MathUtils.generateUUID(), hitCount: 1, end: THREE.MathUtils.generateUUID(), buffer: 0, isUsed: false }], - triggers: [], + actions: { uuid: THREE.MathUtils.generateUUID(), name: 'Action 1', type: 'Start', start: '', hitCount: 1, end: '', buffer: 0 }, connections: { source: { pathUUID: item.modeluuid, pointUUID: pointUUID }, targets: [] }, + speed: 2, }, assetPosition: [...item.position], - speed: 2, }; newPaths.push(newVehiclePath); 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/modules/simulation/path/pathCreation.tsx b/app/src/modules/simulation/path/pathCreation.tsx index 78a4491..33ca2f8 100644 --- a/app/src/modules/simulation/path/pathCreation.tsx +++ b/app/src/modules/simulation/path/pathCreation.tsx @@ -1,27 +1,10 @@ import * as THREE from 'three'; +import * as Types from '../../../types/world/worldTypes'; import { useRef, useState, useEffect } from 'react'; import { Sphere, TransformControls } from '@react-three/drei'; import { useIsConnecting, useRenderDistance, useSelectedActionSphere, useSelectedPath, useSimulationPaths } from '../../../store/store'; import { useFrame, useThree } from '@react-three/fiber'; import { useSubModuleStore } from '../../../store/useModuleStore'; -import { point } from '@turf/helpers'; - -interface ConveyorEventsSchema { - modeluuid: string; - modelName: string; - type: 'Conveyor'; - points: { - uuid: string; - position: [number, number, number]; - rotation: [number, number, number]; - actions: { uuid: string; name: string; type: string; material: string; delay: number | string; spawnInterval: number | string; isUsed: boolean }[] | []; - triggers: { uuid: string; name: string; type: string; isUsed: boolean }[] | []; - connections: { source: { pathUUID: string; pointUUID: string }; targets: { pathUUID: string; pointUUID: string }[] }; - }[]; - assetPosition: [number, number, number]; - assetRotation: [number, number, number]; - speed: number; -} function PathCreation({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObject }) { const { renderDistance } = useRenderDistance(); @@ -89,7 +72,7 @@ function PathCreation({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObject }; } return path; - }) as ConveyorEventsSchema[]; + }) as Types.ConveyorEventsSchema[]; setSimulationPaths(updatedPaths); }; diff --git a/app/src/modules/simulation/simulation.tsx b/app/src/modules/simulation/simulation.tsx index 73ef27f..10934fb 100644 --- a/app/src/modules/simulation/simulation.tsx +++ b/app/src/modules/simulation/simulation.tsx @@ -14,7 +14,7 @@ function Simulation() { const [processes, setProcesses] = useState([]); useEffect(() => { - + console.log('simulationPaths: ', simulationPaths); }, [simulationPaths]); // useEffect(() => { diff --git a/app/src/modules/simulation/simulationtemp/path/pathCreator.tsx b/app/src/modules/simulation/simulationtemp/path/pathCreator.tsx index 1c63289..c09b21c 100644 --- a/app/src/modules/simulation/simulationtemp/path/pathCreator.tsx +++ b/app/src/modules/simulation/simulationtemp/path/pathCreator.tsx @@ -93,6 +93,7 @@ const PathCreator = ({ simulationPaths, setSimulationPaths, connections, setConn intersects = intersects.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") @@ -146,6 +147,7 @@ const PathCreator = ({ simulationPaths, setSimulationPaths, connections, setConn 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") @@ -262,6 +264,7 @@ const PathCreator = ({ simulationPaths, setSimulationPaths, connections, setConn 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") diff --git a/app/src/pages/Dashboard.tsx b/app/src/pages/Dashboard.tsx index 9df13a0..02ce030 100644 --- a/app/src/pages/Dashboard.tsx +++ b/app/src/pages/Dashboard.tsx @@ -1,8 +1,13 @@ import React from 'react'; +import SidePannel from '../components/layout/Dashboard/SidePannel'; +import DashboardHome from '../components/layout/Dashboard/DashboardHome'; const Dashboard: React.FC = () => { return ( -
Dashboard
+
+ + +
); }; 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/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/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) { 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 64be803..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_LOCAL_BASE_URL}`; - +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 f509a5d..71d9c2f 100644 --- a/app/src/services/realTimeVisulization/zoneData/getSelect2dZoneData.ts +++ b/app/src/services/realTimeVisulization/zoneData/getSelect2dZoneData.ts @@ -1,4 +1,4 @@ -let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_LOCAL_BASE_URL}`; +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 ( diff --git a/app/src/services/realTimeVisulization/zoneData/getZoneData.ts b/app/src/services/realTimeVisulization/zoneData/getZoneData.ts index 5d84167..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_LOCAL_BASE_URL}`; - +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 32f850a..100b6ff 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_LOCAL_BASE_URL}`; +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}`, 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..a64f417 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 }), })); @@ -203,6 +206,20 @@ export const useActiveLayer = create((set: any) => ({ setActiveLayer: (x: any) => set({ activeLayer: x }), })); +interface RefTextUpdateState { + refTextupdate: number; + setRefTextUpdate: (callback: (currentValue: number) => number | number) => void; +} + +export const useRefTextUpdate = create((set) => ({ + refTextupdate: -1000, + setRefTextUpdate: (callback) => + set((state) => ({ + refTextupdate: + typeof callback === "function" ? callback(state.refTextupdate) : callback, + })), +})); + export const useResetCamera = create((set: any) => ({ resetCamera: false, setResetCamera: (x: any) => set({ resetCamera: x }), @@ -214,7 +231,7 @@ export const useAddAction = create((set: any) => ({ })); export const useActiveTool = create((set: any) => ({ - activeTool: "Cursor", + activeTool: "cursor", setActiveTool: (x: any) => set({ activeTool: x }), })); @@ -312,7 +329,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 +371,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[]; +} 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/store/useWidgetStore.ts b/app/src/store/useWidgetStore.ts index 115c6aa..047b57b 100644 --- a/app/src/store/useWidgetStore.ts +++ b/app/src/store/useWidgetStore.ts @@ -33,7 +33,7 @@ interface WidgetStore { setDraggedAsset: (asset: Widget | null) => void; // Setter for draggedAsset addWidget: (widget: Widget) => void; // Add a new widget setWidgets: (widgets: Widget[]) => void; // Replace the entire widgets array - setSelectedChartId: (widget: Widget | null) => void; // Set the selected chart/widget + setSelectedChartId: (widget: any | null) => void; // Set the selected chart/widget } // Create the store with Zustand 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 f7bf4b0..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 */ @@ -104,25 +105,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 */ } diff --git a/app/src/styles/components/input.scss b/app/src/styles/components/input.scss index 36ac8ff..e5dd2b4 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,40 +124,37 @@ 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; } } } -.project-dropdowm-container { - position: relative; - height: 32px; - .project-name { - line-height: 32px; - height: 100%; - } -} - .regularDropdown-container { width: 100%; min-width: 80px; @@ -158,6 +163,7 @@ border-radius: 6px; position: relative; cursor: pointer; + .dropdown-header { height: 100%; display: flex; @@ -180,14 +186,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 +212,7 @@ .input.default { width: 100%; position: relative; + .dropdown { top: 3px; right: 3px; @@ -210,6 +220,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 +234,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 +244,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 +263,7 @@ input { background: var(--background-color-secondary); cursor: pointer; } + .active { background: var(--accent-color); } @@ -264,6 +280,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 +318,11 @@ input { display: flex; flex-direction: column; gap: 6px; + .nested-dropdown { margin-left: 0; } + padding: 10px; } @@ -306,7 +331,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 +349,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 +368,7 @@ input { } .icon { - font-size: 12px; + font-size: var(--font-size-small); margin-left: 5px; } } @@ -429,6 +455,7 @@ input { outline: 3px solid var(--accent-color); outline-offset: -3px; transform: translateY(-5px); + &:hover { transform: scale(1.1) translateY(-5px); } @@ -530,6 +557,7 @@ input { .labeled-button-container { @include flex-space-between; padding: 6px 12px; + button { padding: 2px 32px; border: none; @@ -538,6 +566,7 @@ input { background: var(--accent-color); transition: all 0.2s; cursor: pointer; + &:hover { color: var(--primary-color); } @@ -548,12 +577,15 @@ input { margin-bottom: 6px; padding: 6px 12px; @include flex-space-between; + .label { width: 40%; } + .regularDropdown-container { width: 60%; } + .default { width: 60%; } @@ -562,6 +594,7 @@ input { .multi-email-invite-input-container { @include flex-space-between; gap: 20px; + .multi-email-invite-input { width: 100%; display: flex; @@ -571,24 +604,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,12 +634,14 @@ 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); } diff --git a/app/src/styles/components/marketPlace/marketPlace.scss b/app/src/styles/components/marketPlace/marketPlace.scss index 71f5897..620d934 100644 --- a/app/src/styles/components/marketPlace/marketPlace.scss +++ b/app/src/styles/components/marketPlace/marketPlace.scss @@ -47,10 +47,9 @@ border-radius: $border-radius-large; .search-container { - border: none !important; - box-shadow: $box-shadow-medium; border-radius: $border-radius-large; - + outline: 1px solid var(--border-color); + border: none; input { border: none !important; outline: none; @@ -60,6 +59,12 @@ .regularDropdown-container { max-width: 159px; + height: 100%; + border-radius: #{$border-radius-large}; + border: 1px solid var(--border-color); + .dropdown-header { + align-items: center; + } } .button { @@ -128,7 +133,7 @@ justify-content: center; border-radius: #{$border-radius-medium}; overflow: hidden; - img{ + img { height: inherit; width: 100%; object-fit: cover; @@ -143,7 +148,7 @@ display: flex; flex-direction: column; gap: 3px; - .assets-name{ + .assets-name { text-transform: capitalize; } diff --git a/app/src/styles/components/menu/menu.scss b/app/src/styles/components/menu/menu.scss new file mode 100644 index 0000000..8635b0d --- /dev/null +++ b/app/src/styles/components/menu/menu.scss @@ -0,0 +1,163 @@ +@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: 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: 4px; + min-width: 178px; + + .menu-button-container { + position: relative; + height: 100%; + padding: 4px 8px 4px 12px; + border-radius: #{$border-radius-small}; + .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); + rotate: -90deg; + } + } + + .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: 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; + 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); + } + + .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; + padding: 4px; + .submenu-item { + 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(--highlight-accent-color); + 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: var(--highlight-accent-color); + margin: 2px 0; + } +} 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/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/layout/popup.scss b/app/src/styles/layout/popup.scss index a086cf4..9d22e64 100644 --- a/app/src/styles/layout/popup.scss +++ b/app/src/styles/layout/popup.scss @@ -86,7 +86,18 @@ } .project-name, .your-name { + @include flex-center; + gap: 6px; color: var(--accent-color); + .user-profile{ + height: 24px; + width: 24px; + text-align: center; + line-height: 25px; + background-color: var(--accent-color); + color: var(--primary-color); + border-radius: #{$border-radius-circle}; + } } .number-of-peoples-have-access { padding: 4px 12px; @@ -110,3 +121,30 @@ } } } + +.collab-user-live-container{ + @include flex-center; + flex-direction: column; + gap: 6px; + transform: translateY(15px); + .user-image-container{ + height: 30px; + width: 30px; + border-radius: #{$border-radius-circle}; + overflow: hidden; + .user-image{ + height: 100%; + width: 100%; + object-fit: cover; + vertical-align: top; + } + } + .user-name{ + padding: 4px 6px; + border-radius: #{$border-radius-small}; + color: white; + font-size: var(--font-size-regulaar); + font-weight: var(--font-size-regulaar); + text-transform: capitalize; + } +} diff --git a/app/src/styles/layout/sidebar.scss b/app/src/styles/layout/sidebar.scss index d595b92..e0197c3 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; @@ -225,10 +228,13 @@ border-radius: 50%; font-weight: var(--font-weight-bold); color: white; + text-transform: capitalize; } .guest-users-container { display: flex; + width: 100%; + justify-content: flex-end; .other-guest { @include flex-center; @@ -249,7 +255,8 @@ display: flex; .user-organization { - height: 100%; + height: 26px; + width: 52px; max-width: 52px; border-radius: 20px; overflow: hidden; @@ -307,6 +314,25 @@ .sidebar-right-content-container { .dataSideBar { + .inputs-wrapper { + .datas { + + .input-value { + + padding: 5px 10px; + } + + .input-value, + .rename-input { + margin-right: 24px; + width: 170px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + } + } + width: 100%; height: 100%; display: flex; @@ -324,7 +350,7 @@ align-items: center; .multi-level-dropdown { - min-width: 170px; + width: 170px; .dropdown-button { display: flex; @@ -356,6 +382,7 @@ border-bottom: 1px solid var(--border-color); padding-bottom: 6px; } + .inputs-wrapper { display: flex; flex-direction: column; @@ -390,12 +417,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 { @@ -419,7 +446,7 @@ gap: 6px; color: #444; border-radius: 6px; - font-size: 14px; + font-size: var(--font-weight-regular); .infoIcon { padding: 0px 7px; @@ -439,7 +466,7 @@ flex-direction: column; gap: 15px; padding: 0; - font-size: 14px; + font-size: var(--font-weight-regular); color: #4a4a4a; .selectedWidget { @@ -505,6 +532,7 @@ } } } + .machine-mechanics-container { .machine-mechanics-header { padding: 8px 12px; @@ -512,15 +540,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; @@ -528,12 +559,15 @@ cursor: pointer; background: var(--background-color-secondary); border-radius: #{$border-radius-medium}; + path { stroke: var(--accent-color); - strokeWidth: 1.5px; + strokewidth: 1.5px; } + &:hover { background: var(--accent-color); + path { stroke: var(--highlight-accent-color); } @@ -541,11 +575,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; @@ -585,17 +621,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); @@ -615,8 +654,10 @@ cursor: pointer; border-radius: #{$border-radius-small}; transform: translateX(4px); + &:hover { background-color: var(--accent-color); + path { stroke: var(--primary-color); } @@ -637,21 +678,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%; } @@ -665,23 +711,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; @@ -704,20 +756,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); @@ -728,6 +785,7 @@ } } } + .global-properties-container, .analysis-main-container, .asset-properties-container, @@ -738,26 +796,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 { @@ -772,89 +836,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); @@ -866,3 +950,117 @@ } } } + +.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..9d4bde4 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'; @@ -31,6 +32,7 @@ @use 'layout/toast'; // pages +@use 'pages/dashboard.scss'; @use 'pages/home'; @use 'pages/realTimeViz'; @use 'pages/userAuth'; \ No newline at end of file diff --git a/app/src/styles/pages/dashboard.scss b/app/src/styles/pages/dashboard.scss new file mode 100644 index 0000000..3fc9a05 --- /dev/null +++ b/app/src/styles/pages/dashboard.scss @@ -0,0 +1,221 @@ +@use "../abstracts/variables.scss" as *; +@use "../abstracts/mixins.scss" as *; + +.dashboard-main { + height: 100vh; + width: 100vw; + display: flex; + .side-pannel-container { + padding: 32px; + min-width: 240px; + height: 100vh; + display: flex; + flex-direction: column; + gap: 16px; + border-right: 1px solid var(--border-color); + .side-pannel-header { + @include flex-space-between; + .user-container { + @include flex-center; + gap: 6px; + .user-profile { + height: 32px; + width: 32px; + line-height: 32px; + text-align: center; + font-weight: var(--font-weight-medium); + background: var(--accent-color); + color: var(--primary-color); + border-radius: #{$border-radius-circle}; + } + .user-name { + color: var(--accent-color); + } + } + .notifications-container { + @include flex-center; + height: 24px; + width: 24px; + cursor: pointer; + } + } + .new-project-button { + padding: 12px 16px; + cursor: not-allowed; + color: var(--accent-color); + background-color: var(--background-color-secondary); + border-radius: #{$border-radius-large}; + } + .side-bar-content-container { + display: flex; + flex-direction: column; + justify-content: space-between; + height: 100%; + .side-bar-options-container { + .option-list { + display: flex; + align-items: center; + gap: 8px; + padding: 6px 10px; + margin: 4px 0; + border-radius: #{$border-radius-medium}; + &:hover { + background: var(--background-color-secondary); + } + } + .active { + color: var(--accent-color); + font-weight: var(--font-weight-medium); + background-color: var(--highlight-accent-color); + &:hover { + background-color: var(--highlight-accent-color); + } + } + } + } + } + .dashboard-home-container { + width: 100%; + .dashboard-navbar-container { + margin-top: 28px; + padding: 8px 34px 8px 12px; + @include flex-center; + .title { + text-transform: capitalize; + font-size: var(--font-size-large); + width: 100%; + } + .market-place-button { + @include flex-center; + gap: 6px; + padding: 8px 14px; + background: var(--accent-gradient-color); + white-space: nowrap; + border-radius: #{$border-radius-large}; + color: var(--primary-color); + } + .search-wrapper { + width: 400px; + } + } + .container { + margin: 22px 0; + width: 100%; + padding: 0 12px; + .header { + font-size: var(--font-size-large); + } + .cards-container { + display: flex; + flex-wrap: wrap; + position: relative; + width: 100%; + padding: 8px; + gap: 18px; + } + } + } +} + +.dashboard-card-container { + height: 242px; + width: calc((100% / 5) - 23px); + min-width: 260px; + position: relative; + border: 1px solid var(--border-color); + border-radius: #{$border-radius-large}; + overflow: hidden; + .preview-container { + height: 100%; + width: 100%; + img { + height: 100%; + width: 100%; + object-fit: cover; + vertical-align: top; + border: none; + outline: none; + } + } + .project-details-container { + @include flex-space-between; + position: absolute; + bottom: 0; + width: 100%; + padding: 8px 16px; + background: var(--primary-color); + border-radius: 10px; + .project-details { + .project-name { + margin-bottom: 2px; + } + .project-data { + color: var(--accent-color); + } + } + .users-list-container { + @include flex-center; + gap: 6px; + .user-profile { + height: 26px; + width: 26px; + line-height: 26px; + text-align: center; + background-color: var(--accent-color); + color: var(--primary-color); + border-radius: #{$border-radius-circle}; + } + } + } +} + +.market-place-banner-container { + width: 100%; + height: 230px; + overflow: hidden; + position: relative; + padding: 0 24px; + img { + height: 100%; + width: 100%; + object-fit: cover; + border-radius: 30px; + } + .hero-text { + position: absolute; + left: 52px; + bottom: 25px; + font-size: 48px; + font-family: #{$font-roboto}; + font-weight: 800; + color: #ffffff; + text-transform: uppercase; + } + .context { + position: absolute; + top: 20px; + right: 58px; + text-transform: uppercase; + font-size: 22px; + width: 300px; + color: #ffffff; + font-family: #{$font-roboto}; + } + .arrow-context { + position: absolute; + bottom: 27px; + right: 300px; + } + .explore-button { + position: absolute; + top: 95px; + right: 52px; + padding: 10px 20px; + text-transform: uppercase; + font-size: 24px; + border: 1px solid #ffffff; + color: #ffffff; + font-family: #{$font-roboto}; + cursor: pointer; + } +} 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 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; diff --git a/app/src/types/users.d.ts b/app/src/types/users.d.ts index 9e8c35a..55d5361 100644 --- a/app/src/types/users.d.ts +++ b/app/src/types/users.d.ts @@ -8,4 +8,11 @@ export interface User { type AccessOption = { option: string; +}; + +export type ActiveUser = { + _id: string; + userName: string; + email: string; + activeStatus?: string; // Optional property }; \ No newline at end of file diff --git a/app/src/types/world/worldTypes.d.ts b/app/src/types/world/worldTypes.d.ts index 2fcbd4f..31c032c 100644 --- a/app/src/types/world/worldTypes.d.ts +++ b/app/src/types/world/worldTypes.d.ts @@ -295,12 +295,12 @@ interface ConveyorEventsSchema { position: [number, number, number]; rotation: [number, number, number]; actions: { uuid: string; name: string; type: string; material: string; delay: number | string; spawnInterval: number | string; isUsed: boolean }[] | []; - triggers: { uuid: string; name: string; type: string; isUsed: boolean }[] | []; + triggers: { uuid: string; name: string; type: string; isUsed: boolean; bufferTime: number }[] | []; connections: { source: { pathUUID: string; pointUUID: string }; targets: { pathUUID: string; pointUUID: string }[] }; }[]; assetPosition: [number, number, number]; assetRotation: [number, number, number]; - speed: number; + speed: number | string; } interface VehicleEventsSchema { @@ -310,10 +310,9 @@ interface VehicleEventsSchema { point: { uuid: string; position: [number, number, number]; - actions: { uuid: string; name: string; type: string; start: string, hitCount: number, end: string, buffer: number; isUsed: boolean }[] | []; - triggers: { uuid: string; name: string; type: string; isUsed: boolean }[] | []; + actions: { uuid: string; name: string; type: string; start: string, hitCount: number, end: string, buffer: number }; connections: { source: { pathUUID: string; pointUUID: string }; targets: { pathUUID: string; pointUUID: string }[] }; + speed: number; }; assetPosition: [number, number, number]; - speed: number; } \ No newline at end of file