diff --git a/app/src/assets/textures/floor/black.png b/app/src/assets/textures/floor/black.png new file mode 100644 index 0000000..5562e8a Binary files /dev/null and b/app/src/assets/textures/floor/black.png differ diff --git a/app/src/assets/textures/floor/factory wall texture.jpg b/app/src/assets/textures/floor/factory wall texture.jpg new file mode 100644 index 0000000..65e39c3 Binary files /dev/null and b/app/src/assets/textures/floor/factory wall texture.jpg differ diff --git a/app/src/assets/textures/floor/wall-tex.png b/app/src/assets/textures/floor/wall-tex.png new file mode 100644 index 0000000..d1a09a8 Binary files /dev/null and b/app/src/assets/textures/floor/wall-tex.png differ diff --git a/app/src/assets/textures/floor/white.png b/app/src/assets/textures/floor/white.png new file mode 100644 index 0000000..efc1ea4 Binary files /dev/null and b/app/src/assets/textures/floor/white.png differ diff --git a/app/src/components/layout/sidebarLeft/Assets.tsx b/app/src/components/layout/sidebarLeft/Assets.tsx index b863500..f9e4bb5 100644 --- a/app/src/components/layout/sidebarLeft/Assets.tsx +++ b/app/src/components/layout/sidebarLeft/Assets.tsx @@ -1,9 +1,6 @@ import React, { useEffect, useState } from "react"; import Search from "../../ui/inputs/Search"; import { getCategoryAsset } from "../../../services/factoryBuilder/assest/assets/getCategoryAsset"; -import arch from "../../../assets/gltf-glb/arch.glb"; -import door from "../../../assets/gltf-glb/door.glb"; -import window from "../../../assets/gltf-glb/window.glb"; import { fetchAssets } from "../../../services/marketplace/fetchAssets"; import { useSelectedItem } from "../../../store/store"; @@ -16,8 +13,6 @@ import storage from "../../../assets/image/categories/storage.png"; import office from "../../../assets/image/categories/office.png"; import safety from "../../../assets/image/categories/safety.png"; import feneration from "../../../assets/image/categories/feneration.png"; -import archThumbnail from "../../../assets/image/localAssets/arch.png"; -import windowThumbnail from "../../../assets/image/localAssets/window.png"; import SkeletonUI from "../../templates/SkeletonUI"; // ------------------------------------- @@ -90,24 +85,7 @@ const Assets: React.FC = () => { useEffect(() => { setCategoryList([ - { - assetName: "Doors", - assetImage: "", - category: "Feneration", - categoryImage: feneration, - }, - { - assetName: "Windows", - assetImage: "", - category: "Feneration", - categoryImage: feneration, - }, - { - assetName: "Pillars", - assetImage: "", - category: "Feneration", - categoryImage: feneration, - }, + { category: "Fenestration", categoryImage: feneration }, { category: "Vehicles", categoryImage: vehicle }, { category: "Workstation", categoryImage: workStation }, { category: "Machines", categoryImage: machines }, @@ -121,44 +99,15 @@ const Assets: React.FC = () => { const fetchCategoryAssets = async (asset: any) => { setisLoading(true); setSelectedCategory(asset); - if (asset === "Feneration") { - const localAssets: AssetProp[] = [ - { - filename: "arch", - category: "Feneration", - url: arch, - thumbnail: archThumbnail, - tags: "arch", - }, - { - filename: "door", - category: "Feneration", - url: door, - thumbnail: feneration, - tags: "door", - }, - { - filename: "window", - category: "Feneration", - url: window, - thumbnail: windowThumbnail, - tags: "window", - }, - ]; - setCategoryAssets(localAssets); - setFiltereredAssets(localAssets); + try { + const res = await getCategoryAsset(asset); + setCategoryAssets(res); + setFiltereredAssets(res); + setisLoading(false); // End loading + // eslint-disable-next-line + } catch (error) { + echo.error("failed to fetch assets"); setisLoading(false); - } else { - try { - const res = await getCategoryAsset(asset); - setCategoryAssets(res); - setFiltereredAssets(res); - setisLoading(false); // End loading - // eslint-disable-next-line - } catch (error) { - echo.error("failed to fetch assets"); - setisLoading(false); - } } }; @@ -167,64 +116,21 @@ const Assets: React.FC = () => {
- {isLoading ? ( - // Show skeleton when loading - ) : searchValue ? ( -
-
-
-

Results for {searchValue}

-
-
- {categoryAssets?.map((asset: any, index: number) => ( -
- {asset.filename} - -
- {asset.filename - .split("_") - .map( - (word: any) => - word.charAt(0).toUpperCase() + word.slice(1) - ) - .join(" ")} -
+ {(() => { + if (isLoading) { + return ; // Show skeleton when loading + } + if (searchValue) { + return ( +
+
+
+

Results for {searchValue}

- ))} -
-
-
- ) : ( - <> - {selectedCategory ? ( -
-

- {selectedCategory}{" "} -
{ - setSelectedCategory(null); - setCategoryAssets([]); - }} - > - ← Back -
-

-
- {categoryAssets && - categoryAssets?.map((asset: any, index: number) => ( +
+ {categoryAssets?.map((asset: any, index: number) => (
{ src={asset?.thumbnail} alt={asset.filename} className="asset-image" - onPointerDown={() => { - setSelectedItem({ - name: asset.filename, - id: asset.AssetID, - type: - asset.type === "undefined" - ? undefined - : asset.type, - }); - }} /> +
{asset.filename .split("_") @@ -255,40 +152,104 @@ const Assets: React.FC = () => {
))} +
- ) : ( + ); + } + + if (selectedCategory) { + return (
-

Categories

-
- {Array.from( - new Set(categoryList.map((asset) => asset.category)) - ).map((category, index) => { - const categoryInfo = categoryList.find( - (asset) => asset.category === category - ); - return ( -
fetchCategoryAssets(category)} - > - {category} -
{category}
+

+ {selectedCategory} + +

+
+ {categoryAssets?.map((asset: any, index: number) => ( +
+ {asset.filename} { + setSelectedItem({ + name: asset.filename, + id: asset.AssetID, + type: + asset.type === "undefined" + ? undefined + : asset.type, + }); + }} + /> +
+ {asset.filename + .split("_") + .map( + (word: any) => + word.charAt(0).toUpperCase() + word.slice(1) + ) + .join(" ")}
- ); - })} +
+ ))} + {categoryAssets.length === 0 && ( +
+ 🚧 The asset shelf is empty. We're working on filling it + up! +
+ )}
- )} - - )} + ); + } + + return ( +
+

Categories

+
+ {Array.from( + new Set(categoryList.map((asset) => asset.category)) + ).map((category, index) => { + const categoryInfo = categoryList.find( + (asset) => asset.category === category + ); + return ( + + ); + })} +
+
+ ); + })()}
diff --git a/app/src/components/layout/sidebarLeft/Header.tsx b/app/src/components/layout/sidebarLeft/Header.tsx index 70668af..f26f4fa 100644 --- a/app/src/components/layout/sidebarLeft/Header.tsx +++ b/app/src/components/layout/sidebarLeft/Header.tsx @@ -6,7 +6,7 @@ import useToggleStore from "../../../store/useUIToggleStore"; import useModuleStore from "../../../store/useModuleStore"; const Header: React.FC = () => { - const { toggleUI, setToggleUI } = useToggleStore(); + const { toggleUILeft, toggleUIRight, setToggleUI } = useToggleStore(); const { activeModule } = useModuleStore(); return ( @@ -20,15 +20,17 @@ const Header: React.FC = () => { diff --git a/app/src/components/layout/sidebarLeft/SideBarLeft.tsx b/app/src/components/layout/sidebarLeft/SideBarLeft.tsx index 0765b95..eb618ec 100644 --- a/app/src/components/layout/sidebarLeft/SideBarLeft.tsx +++ b/app/src/components/layout/sidebarLeft/SideBarLeft.tsx @@ -12,7 +12,7 @@ import Search from "../../ui/inputs/Search"; const SideBarLeft: React.FC = () => { const [activeOption, setActiveOption] = useState("Widgets"); - const { toggleUI } = useToggleStore(); + const { toggleUILeft } = useToggleStore(); const { activeModule } = useModuleStore(); // Reset activeOption whenever activeModule changes @@ -31,47 +31,55 @@ const SideBarLeft: React.FC = () => { }; return ( -
+
- {toggleUI && ( + {toggleUILeft && (
- {activeModule === "visualization" ? ( - <> - - -
- {activeOption === "Widgets" ? : } -
- - ) : activeModule === "market" ? ( - <> - ) : activeModule === "builder" ? ( - <> - -
- {activeOption === "Outline" ? : } -
- - ) : ( - <> - -
- {activeOption === "Outline" ? : } -
- - )} + {(() => { + if (activeModule === "visualization") { + return ( + <> + + +
+ {activeOption === "Widgets" ? : } +
+ + ); + } else if (activeModule === "market") { + return <>; + } else if (activeModule === "builder") { + return ( + <> + +
+ {activeOption === "Outline" ? : } +
+ + ); + } else { + return ( + <> + +
+ {activeOption === "Outline" ? : } +
+ + ); + } + })()}
)}
diff --git a/app/src/components/layout/sidebarRight/Header.tsx b/app/src/components/layout/sidebarRight/Header.tsx index 3684157..c7c3070 100644 --- a/app/src/components/layout/sidebarRight/Header.tsx +++ b/app/src/components/layout/sidebarRight/Header.tsx @@ -5,10 +5,15 @@ import { ActiveUser } from "../../../types/users"; import CollaborationPopup from "../../templates/CollaborationPopup"; import { getAvatarColor } from "../../../modules/collaboration/functions/getAvatarColor"; import { useSelectedUserStore } from "../../../store/useCollabStore"; +import useToggleStore from "../../../store/useUIToggleStore"; +import { ToggleSidebarIcon } from "../../icons/HeaderIcons"; +import useModuleStore from "../../../store/useModuleStore"; const Header: React.FC = () => { const { activeUsers } = useActiveUsers(); const userName = localStorage.getItem("userName") ?? "Anonymous"; + const { toggleUILeft, toggleUIRight, setToggleUI } = useToggleStore(); + const { activeModule } = useModuleStore(); const guestUsers: ActiveUser[] = activeUsers.filter( (user: ActiveUser) => user.userName !== userName @@ -55,6 +60,25 @@ const Header: React.FC = () => { )}
+
)} - {toggleUI && viewVersionHistory && ( + {toggleUIRight && viewVersionHistory && (
@@ -129,7 +129,7 @@ const SideBarRight: React.FC = () => { )} {/* process builder */} - {toggleUI && + {toggleUIRight && !viewVersionHistory && subModule === "properties" && activeModule !== "visualization" && @@ -140,7 +140,7 @@ const SideBarRight: React.FC = () => {
)} - {toggleUI && + {toggleUIRight && !viewVersionHistory && subModule === "properties" && activeModule !== "visualization" && @@ -152,7 +152,7 @@ const SideBarRight: React.FC = () => {
)} - {toggleUI && + {toggleUIRight && !viewVersionHistory && subModule === "zoneProperties" && (activeModule === "builder" || activeModule === "simulation") && ( @@ -163,7 +163,7 @@ const SideBarRight: React.FC = () => {
)} {/* simulation */} - {toggleUI && !viewVersionHistory && activeModule === "simulation" && ( + {toggleUIRight && !viewVersionHistory && activeModule === "simulation" && ( <> {subModule === "simulations" && (
@@ -189,7 +189,7 @@ const SideBarRight: React.FC = () => { )} {/* realtime visualization */} - {toggleUI && activeModule === "visualization" && } + {toggleUIRight && activeModule === "visualization" && }
); }; diff --git a/app/src/components/templates/SkeletonUI.tsx b/app/src/components/templates/SkeletonUI.tsx index 76c3fbd..2928916 100644 --- a/app/src/components/templates/SkeletonUI.tsx +++ b/app/src/components/templates/SkeletonUI.tsx @@ -7,7 +7,6 @@ interface SkeletonUIProps { // Define the SkeletonUI component const SkeletonUI: React.FC = ({ type }) => { - // Function to render skeleton content based on 'type' const renderSkeleton = () => { switch (type) { @@ -38,17 +37,28 @@ const SkeletonUI: React.FC = ({ type }) => { case "asset": return ( <> -
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
); - + default: return (
diff --git a/app/src/components/ui/FileMenu.tsx b/app/src/components/ui/FileMenu.tsx index 9997ee4..4ad40c5 100644 --- a/app/src/components/ui/FileMenu.tsx +++ b/app/src/components/ui/FileMenu.tsx @@ -32,7 +32,7 @@ const FileMenu: React.FC = () => { }, []); // project - const [projectName, setProjectName] = useState("project 1"); + const [projectName, setProjectName] = useState("Demo Project"); // Load project name from localStorage on mount useEffect(() => { diff --git a/app/src/components/ui/ModuleToggle.tsx b/app/src/components/ui/ModuleToggle.tsx index 63a11ee..bf805c1 100644 --- a/app/src/components/ui/ModuleToggle.tsx +++ b/app/src/components/ui/ModuleToggle.tsx @@ -22,8 +22,11 @@ const ModuleToggle: React.FC = () => { setActiveModule("builder"); setVersionHistory(false); setToggleUI( - localStorage.getItem("navBarUi") - ? localStorage.getItem("navBarUi") === "true" + localStorage.getItem("navBarUiLeft") + ? localStorage.getItem("navBarUiLeft") === "true" + : true, + localStorage.getItem("navBarUiRight") + ? localStorage.getItem("navBarUiRight") === "true" : true ); }} @@ -41,8 +44,11 @@ const ModuleToggle: React.FC = () => { setActiveModule("simulation"); setVersionHistory(false); setToggleUI( - localStorage.getItem("navBarUi") - ? localStorage.getItem("navBarUi") === "true" + localStorage.getItem("navBarUiLeft") + ? localStorage.getItem("navBarUiLeft") === "true" + : true, + localStorage.getItem("navBarUiRight") + ? localStorage.getItem("navBarUiRight") === "true" : true ); }} @@ -60,8 +66,11 @@ const ModuleToggle: React.FC = () => { setActiveModule("visualization"); setVersionHistory(false); setToggleUI( - localStorage.getItem("navBarUi") - ? localStorage.getItem("navBarUi") === "true" + localStorage.getItem("navBarUiLeft") + ? localStorage.getItem("navBarUiLeft") === "true" + : true, + localStorage.getItem("navBarUiRight") + ? localStorage.getItem("navBarUiRight") === "true" : true ); }} @@ -76,7 +85,7 @@ const ModuleToggle: React.FC = () => { onClick={() => { setActiveModule("market"); setVersionHistory(false); - setToggleUI(false); + setToggleUI(false, false); }} >
diff --git a/app/src/components/ui/Tools.tsx b/app/src/components/ui/Tools.tsx index c5457c4..940022f 100644 --- a/app/src/components/ui/Tools.tsx +++ b/app/src/components/ui/Tools.tsx @@ -71,8 +71,11 @@ const Tools: React.FC = () => { // Reset activeTool whenever activeModule changes useEffect(() => { setToggleUI( - localStorage.getItem("navBarUi") - ? localStorage.getItem("navBarUi") === "true" + localStorage.getItem("navBarUiLeft") + ? localStorage.getItem("navBarUiLeft") === "true" + : true, + localStorage.getItem("navBarUiRight") + ? localStorage.getItem("navBarUiRight") === "true" : true ); }, []); @@ -93,8 +96,11 @@ const Tools: React.FC = () => { setToggleView(false); } setToggleUI( - localStorage.getItem("navBarUi") - ? localStorage.getItem("navBarUi") === "true" + localStorage.getItem("navBarUiLeft") + ? localStorage.getItem("navBarUiLeft") === "true" + : true, + localStorage.getItem("navBarUiRight") + ? localStorage.getItem("navBarUiRight") === "true" : true ); setToggleThreeD(!toggleThreeD); @@ -119,7 +125,7 @@ const Tools: React.FC = () => { }, []); useEffect(() => { if (!toggleThreeD) { - setToggleUI(false); + setToggleUI(false, false); } }, [toggleThreeD]); diff --git a/app/src/components/ui/analysis/ROISummary.tsx b/app/src/components/ui/analysis/ROISummary.tsx index 0c4d80e..e0edb74 100644 --- a/app/src/components/ui/analysis/ROISummary.tsx +++ b/app/src/components/ui/analysis/ROISummary.tsx @@ -205,7 +205,7 @@ const ROISummary = ({
-
+ {/*
@@ -224,7 +224,7 @@ const ROISummary = ({ -
+
*/} ) : ( diff --git a/app/src/components/ui/analysis/ThroughputSummary.tsx b/app/src/components/ui/analysis/ThroughputSummary.tsx index 82116cf..8abdf22 100644 --- a/app/src/components/ui/analysis/ThroughputSummary.tsx +++ b/app/src/components/ui/analysis/ThroughputSummary.tsx @@ -5,7 +5,7 @@ import SkeletonUI from "../../templates/SkeletonUI"; const ProductionCapacity = ({ progressPercent = 50, - avgProcessTime = "28.4 Secs/unit", + avgProcessTime = "28.4", machineUtilization = "78%", throughputValue = 128, timeRange = { startTime: "08:00 AM", endTime: "09:00 AM" }, @@ -34,7 +34,7 @@ const ProductionCapacity = ({ <>
- {throughputValue} Units/hour + {avgProcessTime} secs/unit
{/* Dynamic Progress Bar */} @@ -56,8 +56,8 @@ const ProductionCapacity = ({
- Avg. Process Time - {avgProcessTime} + Units/hour + {throughputValue} avg.
Machine Utilization diff --git a/app/src/components/ui/menu/menu.tsx b/app/src/components/ui/menu/menu.tsx index 1fda809..35dae12 100644 --- a/app/src/components/ui/menu/menu.tsx +++ b/app/src/components/ui/menu/menu.tsx @@ -2,6 +2,7 @@ import React, { useState } from "react"; import { ArrowIcon } from "../../icons/ExportCommonIcons"; import { toggleTheme } from "../../../utils/theme"; import useVersionHistoryStore, { useShortcutStore } from "../../../store/store"; +import { useNavigate } from "react-router-dom"; import { useSubModuleStore } from "../../../store/useModuleStore"; interface MenuBarProps { @@ -9,6 +10,7 @@ interface MenuBarProps { } const MenuBar: React.FC = ({ setOpenMenu }) => { + const navigate = useNavigate(); const [activeMenu, setActiveMenu] = useState(null); const [activeSubMenu, setActiveSubMenu] = useState(null); @@ -36,6 +38,12 @@ const MenuBar: React.FC = ({ setOpenMenu }) => { const savedTheme: string | null = localStorage.getItem("theme") ?? "light"; + + const handleLogout = () => { + localStorage.clear(); // 1. Clear all localStorage + navigate('/'); // 2. Redirect to homepage + }; + return (
= ({ setOpenMenu }) => {
)}
+
+
Log out
+
); diff --git a/app/src/modules/builder/geomentries/floors/addFloorToScene.ts b/app/src/modules/builder/geomentries/floors/addFloorToScene.ts index c951ba0..7ee2ceb 100644 --- a/app/src/modules/builder/geomentries/floors/addFloorToScene.ts +++ b/app/src/modules/builder/geomentries/floors/addFloorToScene.ts @@ -2,8 +2,8 @@ import * as THREE from 'three'; import * as Types from "../../../../types/world/worldTypes"; import * as CONSTANTS from "../../../../types/world/worldConstants"; -import texturePath from "../../../../assets/textures/floor/concreteFloorWorn001Diff2k.jpg"; -import normalPath from "../../../../assets/textures/floor/concreteFloorWorn001NorGl2k.jpg"; +import texturePath from "../../../../assets/textures/floor/white.png"; +import texturePathDark from "../../../../assets/textures/floor/black.png"; // Cache for materials const materialCache = new Map(); @@ -14,6 +14,8 @@ export default function addFloorToScene( floorGroup: Types.RefGroup, userData: any, ) { + const savedTheme: string | null = localStorage.getItem('theme'); + const textureLoader = new THREE.TextureLoader(); const textureScale = CONSTANTS.floorConfig.textureScale; @@ -24,20 +26,17 @@ export default function addFloorToScene( if (materialCache.has(materialKey)) { material = materialCache.get(materialKey) as THREE.Material; + // } else { } else { - const floorTexture = textureLoader.load(texturePath); - const normalMap = textureLoader.load(normalPath); + const floorTexture = textureLoader.load(savedTheme === "dark" ? texturePathDark : texturePath); + // const floorTexture = textureLoader.load(texturePath); floorTexture.wrapS = floorTexture.wrapT = THREE.RepeatWrapping; floorTexture.repeat.set(textureScale, textureScale); floorTexture.colorSpace = THREE.SRGBColorSpace; - normalMap.wrapS = normalMap.wrapT = THREE.RepeatWrapping; - normalMap.repeat.set(textureScale, textureScale); - material = new THREE.MeshStandardMaterial({ map: floorTexture, - normalMap: normalMap, side: THREE.DoubleSide, }); diff --git a/app/src/modules/builder/groups/floorItemsGroup.tsx b/app/src/modules/builder/groups/floorItemsGroup.tsx index 1ab9d59..206d00d 100644 --- a/app/src/modules/builder/groups/floorItemsGroup.tsx +++ b/app/src/modules/builder/groups/floorItemsGroup.tsx @@ -1,7 +1,6 @@ import { useFrame, useThree } from "@react-three/fiber"; import { useActiveTool, - useAsset3dWidget, useCamMode, useDeletableFloorItem, useDeleteTool, @@ -14,7 +13,6 @@ import { useToggleView, useTransformMode, } from "../../../store/store"; -import assetVisibility from "../geomentries/assets/assetVisibility"; import { useEffect } from "react"; import * as THREE from "three"; import * as Types from "../../../types/world/worldTypes"; @@ -29,7 +27,6 @@ import loadInitialFloorItems from "../IntialLoad/loadInitialFloorItems"; import addAssetModel from "../geomentries/assets/addAssetModel"; import { getFloorAssets } from "../../../services/factoryBuilder/assest/floorAsset/getFloorItemsApi"; import useModuleStore from "../../../store/useModuleStore"; -// import { retrieveGLTF } from "../../../utils/indexDB/idbUtils"; import { useEventsStore } from "../../../store/simulation/useEventsStore"; const assetManagerWorker = new Worker( @@ -198,9 +195,7 @@ const FloorItemsGroup = ({ }; const startInterval = () => { - if (!intervalId) { - intervalId = setInterval(handleChange, 50); - } + intervalId ??= setInterval(handleChange, 50); }; const stopInterval = () => { diff --git a/app/src/modules/builder/groups/wallsAndWallItems.tsx b/app/src/modules/builder/groups/wallsAndWallItems.tsx index 6b6c97d..455896e 100644 --- a/app/src/modules/builder/groups/wallsAndWallItems.tsx +++ b/app/src/modules/builder/groups/wallsAndWallItems.tsx @@ -1,56 +1,92 @@ import { Geometry } from "@react-three/csg"; -import { useDeleteTool, useSelectedWallItem, useToggleView, useTransformMode, useWallItems, useWalls } from "../../../store/store"; +import { + useDeleteTool, + useSelectedWallItem, + useToggleView, + useTransformMode, + useWallItems, + useWalls, +} from "../../../store/store"; import handleMeshDown from "../eventFunctions/handleMeshDown"; import handleMeshMissed from "../eventFunctions/handleMeshMissed"; import WallsMesh from "./wallsMesh"; import WallItemsGroup from "./wallItemsGroup"; import { useEffect } from "react"; +const WallsAndWallItems = ({ + CSGGroup, + AssetConfigurations, + setSelectedItemsIndex, + selectedItemsIndex, + currentWallItem, + csg, + lines, + hoveredDeletableWallItem, +}: any) => { + const { walls } = useWalls(); + const { wallItems } = useWallItems(); + const { toggleView } = useToggleView(); + const { deleteTool } = useDeleteTool(); + const { transformMode } = useTransformMode(); + const { setSelectedWallItem } = useSelectedWallItem(); -const WallsAndWallItems = ({ CSGGroup, AssetConfigurations, setSelectedItemsIndex, selectedItemsIndex, currentWallItem, csg, lines, hoveredDeletableWallItem }: any) => { - const { walls, setWalls } = useWalls(); - const { wallItems, setWallItems } = useWallItems(); - const { toggleView, setToggleView } = useToggleView(); - const { deleteTool, setDeleteTool } = useDeleteTool(); - const { transformMode, setTransformMode } = useTransformMode(); - const { selectedWallItem, setSelectedWallItem } = useSelectedWallItem(); - - useEffect(() => { - if (transformMode === null) { - if (!deleteTool) { - handleMeshMissed(currentWallItem, setSelectedWallItem, setSelectedItemsIndex); - setSelectedWallItem(null); - setSelectedItemsIndex(null); - } + useEffect(() => { + if (transformMode === null) { + if (!deleteTool) { + handleMeshMissed( + currentWallItem, + setSelectedWallItem, + setSelectedItemsIndex + ); + setSelectedWallItem(null); + setSelectedItemsIndex(null); + } + } + }, [transformMode]); + return ( + { + if (!deleteTool && transformMode !== null) { + handleMeshDown( + event, + currentWallItem, + setSelectedWallItem, + setSelectedItemsIndex, + wallItems, + toggleView + ); } - }, [transformMode]) + }} + onPointerMissed={() => { + if (!deleteTool) { + handleMeshMissed( + currentWallItem, + setSelectedWallItem, + setSelectedItemsIndex + ); + setSelectedWallItem(null); + setSelectedItemsIndex(null); + } + }} + > + + + + + + ); +}; - return ( - { - if (!deleteTool && transformMode !== null) { - handleMeshDown(event, currentWallItem, setSelectedWallItem, setSelectedItemsIndex, wallItems, toggleView); - } - }} - onPointerMissed={() => { - if (!deleteTool) { - handleMeshMissed(currentWallItem, setSelectedWallItem, setSelectedItemsIndex); - setSelectedWallItem(null); - setSelectedItemsIndex(null); - } - }} - > - - - - - - ) -} - -export default WallsAndWallItems; \ No newline at end of file +export default WallsAndWallItems; diff --git a/app/src/modules/builder/groups/wallsMesh.tsx b/app/src/modules/builder/groups/wallsMesh.tsx index 92a0ee3..4f71680 100644 --- a/app/src/modules/builder/groups/wallsMesh.tsx +++ b/app/src/modules/builder/groups/wallsMesh.tsx @@ -1,65 +1,78 @@ -import * as THREE from 'three'; -import * as Types from '../../../types/world/worldTypes'; -import * as CONSTANTS from '../../../types/world/worldConstants'; -import { Base } from '@react-three/csg'; -import { MeshDiscardMaterial } from '@react-three/drei'; -import { useUpdateScene, useWalls } from '../../../store/store'; -import { useEffect } from 'react'; -import { getLines } from '../../../services/factoryBuilder/lines/getLinesApi'; -import objectLinesToArray from '../geomentries/lines/lineConvertions/objectLinesToArray'; -import loadWalls from '../geomentries/walls/loadWalls'; +import * as THREE from "three"; +import * as Types from "../../../types/world/worldTypes"; +import * as CONSTANTS from "../../../types/world/worldConstants"; +import { Base } from "@react-three/csg"; +import { MeshDiscardMaterial } from "@react-three/drei"; +import { useUpdateScene, useWalls } from "../../../store/store"; +import React, { useEffect } from "react"; +import { getLines } from "../../../services/factoryBuilder/lines/getLinesApi"; +import objectLinesToArray from "../geomentries/lines/lineConvertions/objectLinesToArray"; +import loadWalls from "../geomentries/walls/loadWalls"; +// texture +import texturePath from "../../../assets/textures/floor/wall-tex.png"; -const WallsMesh = ({ lines }: any) => { - const { walls, setWalls } = useWalls(); - const { updateScene, setUpdateScene } = useUpdateScene(); +// Cache for materials +const materialCache = new Map(); - useEffect(() => { - if (updateScene) { +const WallsMeshComponent = ({ lines }: any) => { + const { walls, setWalls } = useWalls(); + const { updateScene, setUpdateScene } = useUpdateScene(); - const email = localStorage.getItem('email') - const organization = (email!.split("@")[1]).split(".")[0]; + useEffect(() => { + if (updateScene) { + const email = localStorage.getItem("email"); + const organization = email!.split("@")[1].split(".")[0]; - getLines(organization).then((data) => { - const Lines: Types.Lines = objectLinesToArray(data); - localStorage.setItem("Lines", JSON.stringify(Lines)); + getLines(organization).then((data) => { + const Lines: Types.Lines = objectLinesToArray(data); + localStorage.setItem("Lines", JSON.stringify(Lines)); - if (Lines) { - loadWalls(lines, setWalls); - } - }) - setUpdateScene(false); + if (Lines) { + loadWalls(lines, setWalls); } - }, [updateScene]) + }); + setUpdateScene(false); + } + }, [updateScene]); - return ( - <> - {walls.map((wall: Types.Wall, index: number) => ( - - - - - - - - - ))} - - ) -} + const textureLoader = new THREE.TextureLoader(); + const wallTexture = textureLoader.load(texturePath); -export default WallsMesh; \ No newline at end of file + wallTexture.wrapS = wallTexture.wrapT = THREE.RepeatWrapping; + wallTexture.repeat.set(0.1, 0.1); + wallTexture.colorSpace = THREE.SRGBColorSpace; + + return ( + <> + {walls.map((wall: Types.Wall, index: number) => ( + + + + + + + + + ))} + + ); +}; + +const WallsMesh = React.memo(WallsMeshComponent); +export default WallsMesh; diff --git a/app/src/modules/scene/controls/selectionControls/moveControls.tsx b/app/src/modules/scene/controls/selectionControls/moveControls.tsx index 498056a..18af918 100644 --- a/app/src/modules/scene/controls/selectionControls/moveControls.tsx +++ b/app/src/modules/scene/controls/selectionControls/moveControls.tsx @@ -49,7 +49,7 @@ function MoveControls({ "Ctrl" | "Shift" | "Ctrl+Shift" | "" >(""); const email = localStorage.getItem("email"); - const organization = email!.split("@")[1].split(".")[0]; + const organization = email?.split("@")[1].split(".")[0] ?? null; const updateBackend = ( productName: string, @@ -308,7 +308,7 @@ function MoveControls({ } ); - if (event) { + if (event && organization) { updateBackend( selectedProduct.productName, selectedProduct.productId, diff --git a/app/src/modules/scene/controls/selectionControls/rotateControls.tsx b/app/src/modules/scene/controls/selectionControls/rotateControls.tsx index 58dab0c..31ba8c9 100644 --- a/app/src/modules/scene/controls/selectionControls/rotateControls.tsx +++ b/app/src/modules/scene/controls/selectionControls/rotateControls.tsx @@ -22,7 +22,7 @@ function RotateControls({ rotatedObjects, setRotatedObjects, movedObjects, setMo const itemsData = useRef([]); const email = localStorage.getItem('email') - const organization = (email!.split("@")[1]).split(".")[0]; + const organization = (email?.split("@")[1])?.split(".")[0] ?? null; const updateBackend = ( productName: string, @@ -214,7 +214,7 @@ function RotateControls({ rotatedObjects, setRotatedObjects, movedObjects, setMo rotation: [obj.rotation.x, obj.rotation.y, obj.rotation.z], }) - if (event) { + if (event && organization) { updateBackend( selectedProduct.productName, selectedProduct.productId, diff --git a/app/src/modules/visualization/RealTimeVisulization.tsx b/app/src/modules/visualization/RealTimeVisulization.tsx index 23f2532..bbc996f 100644 --- a/app/src/modules/visualization/RealTimeVisulization.tsx +++ b/app/src/modules/visualization/RealTimeVisulization.tsx @@ -187,6 +187,7 @@ const RealTimeVisulization: React.FC = () => {
void; // Action to update toggleUI + toggleUILeft: boolean; + toggleUIRight: boolean; + setToggleUI: (value1: boolean, value2: boolean) => void; } const useToggleStore = create((set) => ({ - toggleUI: true, // Initial state - setToggleUI: (value: boolean) => set({ toggleUI: value }), // Update the state + toggleUILeft: true, + toggleUIRight: false, + setToggleUI: (value1: boolean, value2: boolean) => { + set({ toggleUILeft: value1, toggleUIRight: value2 }); + }, })); export default useToggleStore; diff --git a/app/src/styles/layout/sidebar.scss b/app/src/styles/layout/sidebar.scss index 02c5c3a..9317bd6 100644 --- a/app/src/styles/layout/sidebar.scss +++ b/app/src/styles/layout/sidebar.scss @@ -1,6 +1,55 @@ @use "../abstracts/variables" as *; @use "../abstracts/mixins" as *; +.toggle-sidebar-ui-button { + @include flex-center; + cursor: pointer; + height: 32px; + width: 32px; + min-height: 32px; + min-width: 32px; + border-radius: #{$border-radius-large}; + position: relative; + + .tooltip { + top: 6px; + right: -168px; + + &::after { + left: 0px; + bottom: 50%; + } + } + + &:hover { + outline: 1px solid var(--border-color); + outline-offset: -1px; + background: var(--background-color-solid); + + .tooltip { + opacity: 1; + transform: translateX(2px); + } + } +} + +.toggle-sidebar-ui-button.active { + background: var(--background-color-accent); + + rect { + stroke: var(--icon-default-color-active); + } + + circle { + fill: var(--icon-default-color-active); + } + + &:hover { + filter: saturate(0.8); + background: var(--background-color-accent); + } +} + .sidebar-left-wrapper { width: 270px; position: fixed; @@ -34,15 +83,6 @@ } .toggle-sidebar-ui-button { - @include flex-center; - cursor: pointer; - height: 32px; - width: 32px; - min-height: 32px; - min-width: 32px; - border-radius: #{$border-radius-large}; - position: relative; - .tooltip { top: 6px; right: -168px; @@ -52,34 +92,6 @@ bottom: 50%; } } - - &:hover { - outline: 1px solid var(--border-color); - outline-offset: -1px; - background: var(--background-color-solid); - - .tooltip { - opacity: 1; - transform: translateX(2px); - } - } - } - - .active { - background: var(--background-color-accent); - - rect { - stroke: var(--icon-default-color-active); - } - - circle { - fill: var(--icon-default-color-active); - } - - &:hover { - filter: saturate(0.8); - background: var(--background-color-accent); - } } } @@ -295,7 +307,7 @@ padding: 10px; padding-left: 16px; width: 100%; - gap: 12px; + gap: 8px; height: 52px; .options-container { @@ -318,7 +330,7 @@ .split { height: 20px; - width: 2px; + min-width: 1px; background: var(--text-disabled); } @@ -1166,8 +1178,7 @@ height: 100%; width: 1px; position: absolute; - color: var(--text-color); - opacity: 0.4; + color: var(--accent-color); font-size: var(--font-size-regular); outline-offset: -1px; top: 0; @@ -1431,6 +1442,18 @@ } } } + .toggle-sidebar-ui-button { + svg { + transform: scaleX(-1); + } + .tooltip { + right: 56px; + &::after { + left: 100%; + bottom: 50%; + } + } + } } .assets-container-main { @@ -1598,7 +1621,11 @@ height: 100%; gap: 6px; padding: 2px; - + .no-asset { + text-align: center; + margin: 12px; + width: 100%; + } .assets { width: 122px; height: 95px; @@ -1655,20 +1682,6 @@ } } -.skeleton-wrapper { - display: flex; - - .asset-name { - width: 40%; - height: 10px; - } - - .asset { - width: 100%; - height: 100%; - } -} - .sidebar-left-wrapper, .sidebar-right-wrapper { transition: height 0.2s ease-in-out; diff --git a/app/src/styles/layout/skeleton.scss b/app/src/styles/layout/skeleton.scss index 9df0331..9dbc069 100644 --- a/app/src/styles/layout/skeleton.scss +++ b/app/src/styles/layout/skeleton.scss @@ -1,61 +1,90 @@ .skeleton-wrapper { - // max-width: 600px; - margin: 0 auto; - width: 100%; + margin: 0 auto; + width: 100%; - .skeleton { - background: var(--background-color-gray); + .skeleton { + background: var(--background-color-gray); - border-radius: 8px; - position: relative; - overflow: hidden; + border-radius: 8px; + position: relative; + overflow: hidden; - &::after { - content: ''; - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - background-image: linear-gradient(90deg, - rgba(255, 255, 255, 0) 0%, - rgba(255, 255, 255, 0.2) 20%, - rgba(255, 255, 255, 0.5) 60%, - rgba(255, 255, 255, 0) 100%); - transform: translateX(-100%); - animation: shimmer 1.5s infinite; - } + &::after { + content: ""; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-image: linear-gradient( + 90deg, + rgba(255, 255, 255, 0) 0%, + rgba(255, 255, 255, 0.2) 20%, + rgba(255, 255, 255, 0.39) 60%, + rgba(255, 255, 255, 0) 100% + ); + transform: translateX(-100%); + animation: shimmer 1.5s infinite; + } + } + + .skeleton-header { + margin-bottom: 20px; + + .skeleton-title { + width: 100%; + height: 25px; + margin-bottom: 12px; } - .skeleton-header { - margin-bottom: 20px; - - .skeleton-title { - width: 100%; - height: 25px; - margin-bottom: 12px; - } - - .skeleton-subtitle { - width: 100%; - height: 4px; - } + .skeleton-subtitle { + width: 100%; + height: 4px; } + } - .skeleton-content { - display: flex; - flex-direction: column; - gap: 16px; + .skeleton-content { + display: flex; + flex-direction: column; + gap: 16px; - .skeleton-card { - width: 100%; - height: 15px; - } + .skeleton-card { + width: 100%; + height: 15px; } + } +} + +.asset-category-title{ + width: 60%; + height: 12px; + margin-bottom: 12px; + margin-top: 4px; +} +.skeleton-content-asset{ + display: flex; + height: calc(95px * 2 + 10px); + gap: 10px; + flex-wrap: wrap; + .skeleton-content { + gap: 8px; + flex-direction: column; + min-width: 122px; + min-height: 95px; + .asset-name { + width: 40%; + height: 10px; + } + .asset { + flex: 1; + width: 100%; + height: 100%; + } + } } @keyframes shimmer { - 100% { - transform: translateX(100%); - } -} \ No newline at end of file + 100% { + transform: translateX(100%); + } +} diff --git a/app/src/types/world/worldConstants.ts b/app/src/types/world/worldConstants.ts index 0374c8a..aaa8466 100644 --- a/app/src/types/world/worldConstants.ts +++ b/app/src/types/world/worldConstants.ts @@ -326,7 +326,7 @@ export const lineConfig: LineConfig = { }; export const wallConfig: WallConfig = { - defaultColor: "white", // Default color of the walls + defaultColor: "#f2f2f2", // Default color of the walls height: 7, // Height of the walls width: 0.05, // Width of the walls }; @@ -334,7 +334,7 @@ export const wallConfig: WallConfig = { export const floorConfig: FloorConfig = { defaultColor: "grey", // Default color of the floors height: 0.1, // Height of the floors - textureScale: 0.1, // Scale of the floor texture + textureScale: 1, // Scale of the floor texture }; export const roofConfig: RoofConfig = { @@ -345,7 +345,7 @@ export const roofConfig: RoofConfig = { export const aisleConfig: AisleConfig = { width: 0.1, // Width of the aisles height: 0.01, // Height of the aisles - defaultColor: 0xffff00, // Default color of the aisles + defaultColor: 0xE2AC09, // Default color of the aisles }; export const zoneConfig: ZoneConfig = { diff --git a/app/src/utils/shortcutkeys/handleShortcutKeys.ts b/app/src/utils/shortcutkeys/handleShortcutKeys.ts index 2ff5f39..c3346b8 100644 --- a/app/src/utils/shortcutkeys/handleShortcutKeys.ts +++ b/app/src/utils/shortcutkeys/handleShortcutKeys.ts @@ -17,7 +17,7 @@ import { useSelectedZoneStore } from "../../store/visualization/useZoneStore"; const KeyPressListener: React.FC = () => { const { activeModule, setActiveModule } = useModuleStore(); const { setActiveSubTool } = useActiveSubTool(); - const { toggleUI, setToggleUI } = useToggleStore(); + const { toggleUILeft, toggleUIRight, setToggleUI } = useToggleStore(); const { setToggleThreeD } = useThreeDStore(); const { setToolMode } = useToolMode(); const { setIsPlaying } = usePlayButtonStore(); @@ -26,7 +26,7 @@ const KeyPressListener: React.FC = () => { const { setAddAction } = useAddAction(); const { setSelectedWallItem } = useSelectedWallItem(); const { setActiveTool } = useActiveTool(); - const { clearSelectedZone} = useSelectedZoneStore(); + const { clearSelectedZone } = useSelectedZoneStore(); const isTextInput = (element: Element | null): boolean => element instanceof HTMLInputElement || @@ -42,9 +42,10 @@ const KeyPressListener: React.FC = () => { }; const module = modules[keyCombination]; if (module && !toggleView) { + console.log("hi"); setActiveTool("cursor"); setActiveSubTool("cursor"); - if (module === "market") setToggleUI(false); + if (module === "market") setToggleUI(false, false); setActiveModule(module); } }; @@ -69,6 +70,7 @@ const KeyPressListener: React.FC = () => { const toggleTo2D = toggleView; setToggleView(!toggleTo2D); setToggleThreeD(toggleTo2D); + setToggleUI(toggleTo2D, toggleTo2D); if (toggleTo2D) { setSelectedWallItem(null); setDeleteTool(false); @@ -105,6 +107,29 @@ const KeyPressListener: React.FC = () => { }; + const handleSidebarShortcuts = (key: string) => { + if (activeModule !== "market") { + if (key === "Ctrl+\\") { + if (toggleUILeft === toggleUIRight) { + setToggleUI(!toggleUILeft, !toggleUIRight); + } + else { + setToggleUI(true, true); + } + return; + } + if (key === "Ctrl+]") { + setToggleUI(toggleUILeft, !toggleUIRight); + return; + } + if (key === "Ctrl+[") { + setToggleUI(!toggleUILeft, toggleUIRight); + return; + } + } + } + + const handleKeyPress = (event: KeyboardEvent) => { if (isTextInput(document.activeElement)) return; @@ -113,11 +138,8 @@ const KeyPressListener: React.FC = () => { event.preventDefault(); - if (keyCombination === "Ctrl+\\") { - if (activeModule !== "market") setToggleUI(!toggleUI); - return; - } - + // Shortcuts specific for sidebar visibility toggle and others specific to sidebar if added + handleSidebarShortcuts(keyCombination); // Active module selection (builder, simulation, etc.) handleModuleSwitch(keyCombination); // Common editing tools: cursor | delete | free-hand @@ -132,6 +154,7 @@ const KeyPressListener: React.FC = () => { if (keyCombination === "ESCAPE") { setActiveTool("cursor"); + setActiveSubTool("cursor"); setIsPlaying(false); clearSelectedZone(); } @@ -146,7 +169,7 @@ const KeyPressListener: React.FC = () => { window.addEventListener("keydown", handleKeyPress); return () => window.removeEventListener("keydown", handleKeyPress); // eslint-disable-next-line react-hooks/exhaustive-deps - }, [activeModule, toggleUI, toggleView]); + }, [activeModule, toggleUIRight, toggleUILeft, toggleView]); return null; }; diff --git a/app/src/utils/theme.ts b/app/src/utils/theme.ts index 9395129..541f4ba 100644 --- a/app/src/utils/theme.ts +++ b/app/src/utils/theme.ts @@ -4,7 +4,7 @@ export { }; function setTheme() { const savedTheme: string | null = localStorage.getItem('theme'); const systemPrefersDark: boolean = window.matchMedia('(prefers-color-scheme: dark)').matches; - const defaultTheme: string = savedTheme || (systemPrefersDark ? 'dark' : 'light'); + const defaultTheme: string = savedTheme ?? (systemPrefersDark ? 'dark' : 'light'); document.documentElement.setAttribute('data-theme', defaultTheme); localStorage.setItem('theme', defaultTheme); }