From 67b942ac16f802f7eba92fd29845c4a40b1f83d6 Mon Sep 17 00:00:00 2001 From: Jerald-Golden-B Date: Sat, 23 Aug 2025 17:52:15 +0530 Subject: [PATCH 01/34] bug fix wallAsset --- app/src/modules/builder/wall/Instances/instance/wall.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/modules/builder/wall/Instances/instance/wall.tsx b/app/src/modules/builder/wall/Instances/instance/wall.tsx index 9d68d73..8798b01 100644 --- a/app/src/modules/builder/wall/Instances/instance/wall.tsx +++ b/app/src/modules/builder/wall/Instances/instance/wall.tsx @@ -121,7 +121,7 @@ function Wall({ wall }: { readonly wall: Wall }) { castShadow receiveShadow ref={meshRef} - geometry={geometry} + geometry={visible ? geometry : new THREE.BoxGeometry(0, 0)} position={[centerX, centerY, centerZ]} rotation={[0, -angle, 0]} userData={wall} From 358ce22767cb4502860ba25f22e734c3b0c30b54 Mon Sep 17 00:00:00 2001 From: Gomathi9520 Date: Mon, 25 Aug 2025 18:13:57 +0530 Subject: [PATCH 02/34] feat: implement asset thumbnail fetching and update Hrm and AssetManagement components to use asset data --- .../resourceManagement/hrm/Hrm.tsx | 42 +++------ .../hrm/assetManagement/AssetManagement.tsx | 93 ++++++++++--------- .../asset/assets/getAssetThumbnail.ts | 33 +++++++ 3 files changed, 95 insertions(+), 73 deletions(-) create mode 100644 app/src/services/factoryBuilder/asset/assets/getAssetThumbnail.ts diff --git a/app/src/components/layout/sidebarRight/resourceManagement/hrm/Hrm.tsx b/app/src/components/layout/sidebarRight/resourceManagement/hrm/Hrm.tsx index 99828ae..439879e 100644 --- a/app/src/components/layout/sidebarRight/resourceManagement/hrm/Hrm.tsx +++ b/app/src/components/layout/sidebarRight/resourceManagement/hrm/Hrm.tsx @@ -1,29 +1,21 @@ import { useEffect, useState } from 'react' import { ClockThreeIcon, LocationPinIcon, TargetIcon } from '../../../../icons/ExportCommonIcons' import { useSceneContext } from '../../../../../modules/scene/sceneContext'; -import { useProductContext } from '../../../../../modules/simulation/products/productContext'; import RenameInput from '../../../../ui/inputs/RenameInput'; import { useResourceManagementId } from '../../../../../store/builder/store'; -import { set } from 'immer/dist/internal'; // import NavigateCatagory from '../NavigateCatagory' const Hrm = () => { const [selectedCard, setSelectedCard] = useState(0); const [workers, setWorkers] = useState([]); - - const { productStore } = useSceneContext(); - const { products, getProductById } = productStore(); - const { selectedProductStore } = useProductContext(); - const { selectedProduct } = selectedProductStore(); const { setResourceManagementId } = useResourceManagementId(); + const { assetStore } = useSceneContext(); + const { assets: allAssets } = assetStore(); useEffect(() => { - if (selectedProduct) { - const productDetails = getProductById(selectedProduct.productUuid); - const workerDetails = productDetails?.eventDatas || []; - - const formattedWorkers = workerDetails - .filter((worker: any) => worker.type === "human") + if (allAssets.length > 0) { + const formattedWorkers = allAssets + .filter((worker: any) => worker.eventData.type === "Human") .map((worker: any, index: number) => ({ employee: { image: "", @@ -34,15 +26,15 @@ const Hrm = () => { }, task: { status: "Ongoing", - title: worker.taskTitle || "No Task Assigned", + title: worker.taskTitle ?? "No Task Assigned", location: { - floor: worker.floor || 0, - zone: worker.zone || "N/A" + floor: worker.floor ?? 0, + zone: worker.zone ?? "N/A" }, - planned_time_hours: worker.plannedTime || 0, - time_spent_hours: worker.timeSpent || 0, - total_tasks: worker.totalTasks || 0, - completed_tasks: worker.completedTasks || 0 + planned_time_hours: worker.plannedTime ?? 0, + time_spent_hours: worker.timeSpent ?? 0, + total_tasks: worker.totalTasks ?? 0, + completed_tasks: worker.completedTasks ?? 0 }, actions: [ "Assign Task", @@ -55,13 +47,7 @@ const Hrm = () => { setWorkers(formattedWorkers); } - }, [selectedProduct, getProductById]); - - useEffect(() => { - // - }, [workers]); - - + }, [allAssets]); // const employee_details = [ @@ -151,7 +137,7 @@ const Hrm = () => { // }, // ] function handleRenameWorker(newName: string) { - // + } function handleHumanClick(employee: any) { diff --git a/app/src/components/layout/sidebarRight/resourceManagement/hrm/assetManagement/AssetManagement.tsx b/app/src/components/layout/sidebarRight/resourceManagement/hrm/assetManagement/AssetManagement.tsx index faa6015..c7ed11f 100644 --- a/app/src/components/layout/sidebarRight/resourceManagement/hrm/assetManagement/AssetManagement.tsx +++ b/app/src/components/layout/sidebarRight/resourceManagement/hrm/assetManagement/AssetManagement.tsx @@ -3,60 +3,65 @@ import { useEffect, useState } from 'react' import { EyeIcon, ForkLiftIcon, KebabIcon, LocationPinIcon, RightHalfFillCircleIcon } from '../../../../../icons/ExportCommonIcons'; import assetImage from "../../../../../../assets/image/asset-image.png" import { useSceneContext } from '../../../../../../modules/scene/sceneContext'; -import { useProductContext } from '../../../../../../modules/simulation/products/productContext'; import RenameInput from '../../../../../ui/inputs/RenameInput'; import { useResourceManagementId } from '../../../../../../store/builder/store'; +import { getAssetThumbnail } from '../../../../../../services/factoryBuilder/asset/assets/getAssetThumbnail'; + const AssetManagement = () => { // const [selectedCategory, setSelectedCategory] = useState("All Assets"); const [expandedAssetId, setExpandedAssetId] = useState(null); const [assets, setAssets] = useState([]); - - const { productStore } = useSceneContext(); - const { products, getProductById } = productStore(); - const { selectedProductStore } = useProductContext(); - const { selectedProduct } = selectedProductStore(); const { setResourceManagementId } = useResourceManagementId(); + const { assetStore } = useSceneContext(); + const { assets: allAssets } = assetStore(); - + async function getAsset(assetId: string) { + let thumbnail = await getAssetThumbnail(assetId) + if (thumbnail.thumbnail) { + let assetImage = thumbnail.thumbnail + return assetImage; + } + } useEffect(() => { - if (selectedProduct) { - const productDetails = getProductById(selectedProduct.productUuid); - const productAssets = productDetails?.eventDatas || []; - const grouped: Record = {}; - productAssets.forEach((asset: any) => { - if (asset.type === "storageUnit" || asset.type === "human") return; - if (!grouped[asset.modelName]) { - grouped[asset.modelName] = { - id: asset.modelUuid, - name: asset.modelName, - model: asset.modelCode || "N/A", - status: asset.status || "Online", - usageRate: asset.usageRate || 15, - level: asset.level || "Level 1", - image: assetImage, - description: asset.description || "No description", - cost: asset.cost || 0, - count: 1, - }; - } else { - grouped[asset.modelName].count += 1; - } - }); + if (allAssets.length > 0) { + const fetchAssets = async () => { + const grouped: Record = {}; - setAssets(Object.values(grouped)); + // Use Promise.all to handle all async operations + await Promise.all(allAssets.map(async (asset: any) => { + console.log('asset: ', asset); + if (asset.eventData.type === "Storage" || asset.eventData.type === "Human") return; + + const assetImage = await getAsset(asset.assetId); + + if (!grouped[asset.assetId]) { + grouped[asset.assetId] = { + id: asset.modelUuid, + assetId: asset.assetId, + name: asset.modelName, + model: asset.modelCode ?? "N/A", + status: asset.status ?? "Online", + usageRate: asset.usageRate ?? 15, + level: asset.level ?? "Level 1", + image: assetImage, + description: asset.description ?? "No description", + cost: asset.cost ?? 0, + count: 1, + }; + } else { + grouped[asset.assetId].count += 1; + } + })); + + setAssets(Object.values(grouped)); + } + fetchAssets(); } - }, [selectedProduct]); + }, [allAssets]); function handleRenameAsset(newName: string) { - // - // if (expandedAssetId) { - // setAssets(prevAssets => - // prevAssets.map(asset => - // asset.id === expandedAssetId ? { ...asset, name: newName } : asset - // ) - // ); - // } + } useEffect(() => { @@ -65,8 +70,6 @@ const AssetManagement = () => { }, [assets]); function handleAssetClick(id: string) { - - setResourceManagementId(id); } @@ -127,7 +130,7 @@ const AssetManagement = () => { {expandedAssetId === asset.id ? <>
setExpandedAssetId(null)}>▾
- + { console.log(asset.image) }} /> :
@@ -170,9 +173,9 @@ const AssetManagement = () => { -
+
handleAssetClick(asset.id)}> -
handleAssetClick(asset.id)}>View in Scene
+
View in Scene
diff --git a/app/src/services/factoryBuilder/asset/assets/getAssetThumbnail.ts b/app/src/services/factoryBuilder/asset/assets/getAssetThumbnail.ts new file mode 100644 index 0000000..7a318a2 --- /dev/null +++ b/app/src/services/factoryBuilder/asset/assets/getAssetThumbnail.ts @@ -0,0 +1,33 @@ +let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`; + +export const getAssetThumbnail = async (assetId: String) => { + console.log('assetId: ', assetId); + try { + const response = await fetch( + `${url_Backend_dwinzo}/api/v2/getAssetThumbnail/${assetId}`, + { + method: "GET", + headers: { + Authorization: "Bearer ", + "Content-Type": "application/json", + token: localStorage.getItem("token") || "", + refresh_token: localStorage.getItem("refreshToken") || "", + }, + } + ); + const newAccessToken = response.headers.get("x-access-token"); + if (newAccessToken) { + localStorage.setItem("token", newAccessToken); + } + + if (!response.ok) { + throw new Error("Failed to fetch assets"); + } + + // console.log('response: ', response); + return await response.json(); + } catch (error: any) { + echo.error("Failed to get asset image"); + console.log(error.message); + } +}; From 6026c3b82b52678f8967a1056ec0ccbee1c0ca39 Mon Sep 17 00:00:00 2001 From: Gomathi9520 Date: Mon, 25 Aug 2025 18:16:02 +0530 Subject: [PATCH 03/34] refactor: remove unnecessary console logs in asset management and thumbnail fetching --- .../hrm/assetManagement/AssetManagement.tsx | 4 ++-- .../factoryBuilder/asset/assets/getAssetThumbnail.ts | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/src/components/layout/sidebarRight/resourceManagement/hrm/assetManagement/AssetManagement.tsx b/app/src/components/layout/sidebarRight/resourceManagement/hrm/assetManagement/AssetManagement.tsx index c7ed11f..6715d33 100644 --- a/app/src/components/layout/sidebarRight/resourceManagement/hrm/assetManagement/AssetManagement.tsx +++ b/app/src/components/layout/sidebarRight/resourceManagement/hrm/assetManagement/AssetManagement.tsx @@ -30,7 +30,7 @@ const AssetManagement = () => { // Use Promise.all to handle all async operations await Promise.all(allAssets.map(async (asset: any) => { - console.log('asset: ', asset); + if (asset.eventData.type === "Storage" || asset.eventData.type === "Human") return; const assetImage = await getAsset(asset.assetId); @@ -130,7 +130,7 @@ const AssetManagement = () => { {expandedAssetId === asset.id ? <>
setExpandedAssetId(null)}>▾
- { console.log(asset.image) }} /> + :
diff --git a/app/src/services/factoryBuilder/asset/assets/getAssetThumbnail.ts b/app/src/services/factoryBuilder/asset/assets/getAssetThumbnail.ts index 7a318a2..77469a0 100644 --- a/app/src/services/factoryBuilder/asset/assets/getAssetThumbnail.ts +++ b/app/src/services/factoryBuilder/asset/assets/getAssetThumbnail.ts @@ -1,7 +1,7 @@ let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`; export const getAssetThumbnail = async (assetId: String) => { - console.log('assetId: ', assetId); + try { const response = await fetch( `${url_Backend_dwinzo}/api/v2/getAssetThumbnail/${assetId}`, @@ -24,10 +24,10 @@ export const getAssetThumbnail = async (assetId: String) => { throw new Error("Failed to fetch assets"); } - // console.log('response: ', response); + // return await response.json(); } catch (error: any) { echo.error("Failed to get asset image"); - console.log(error.message); + } }; From 6fa4d5323d637aa6aa67a7a090cc8b25cef34fe5 Mon Sep 17 00:00:00 2001 From: Gomathi9520 Date: Tue, 26 Aug 2025 09:36:09 +0530 Subject: [PATCH 04/34] feat: add asset thumbnail fetching and update worker data display in Hrm component --- .../resourceManagement/hrm/Hrm.tsx | 389 +++++++++--------- app/src/styles/layout/resourceManagement.scss | 6 + 2 files changed, 208 insertions(+), 187 deletions(-) diff --git a/app/src/components/layout/sidebarRight/resourceManagement/hrm/Hrm.tsx b/app/src/components/layout/sidebarRight/resourceManagement/hrm/Hrm.tsx index 439879e..efb5cf3 100644 --- a/app/src/components/layout/sidebarRight/resourceManagement/hrm/Hrm.tsx +++ b/app/src/components/layout/sidebarRight/resourceManagement/hrm/Hrm.tsx @@ -3,6 +3,7 @@ import { ClockThreeIcon, LocationPinIcon, TargetIcon } from '../../../../icons/E import { useSceneContext } from '../../../../../modules/scene/sceneContext'; import RenameInput from '../../../../ui/inputs/RenameInput'; import { useResourceManagementId } from '../../../../../store/builder/store'; +import { getAssetThumbnail } from '../../../../../services/factoryBuilder/asset/assets/getAssetThumbnail'; // import NavigateCatagory from '../NavigateCatagory' const Hrm = () => { @@ -12,174 +13,188 @@ const Hrm = () => { const { assetStore } = useSceneContext(); const { assets: allAssets } = assetStore(); - useEffect(() => { - if (allAssets.length > 0) { - const formattedWorkers = allAssets - .filter((worker: any) => worker.eventData.type === "Human") - .map((worker: any, index: number) => ({ - employee: { - image: "", - name: worker.modelName, - modelId: worker.modelUuid, - employee_id: `HR-${204 + index}`, - status: "Active", - }, - task: { - status: "Ongoing", - title: worker.taskTitle ?? "No Task Assigned", - location: { - floor: worker.floor ?? 0, - zone: worker.zone ?? "N/A" + async function getAsset(assetId: string) { + let thumbnail = await getAssetThumbnail(assetId) + if (thumbnail.thumbnail) { + let assetImage = thumbnail.thumbnail + return assetImage; + } + } + + useEffect(() => { + if (allAssets.length > 0) { + const fetchWorkers = async () => { + const humans = allAssets.filter((worker: any) => worker.eventData.type === "Human"); + + const formattedWorkers = await Promise.all( + humans.map(async (worker: any, index: number) => { + const assetImage = await getAsset(worker.assetId); + + return { + employee: { + image: assetImage, + name: worker.modelName, + modelId: worker.modelUuid, + employee_id: `HR-${204 + index}`, + status: "Active", }, - planned_time_hours: worker.plannedTime ?? 0, - time_spent_hours: worker.timeSpent ?? 0, - total_tasks: worker.totalTasks ?? 0, - completed_tasks: worker.completedTasks ?? 0 - }, - actions: [ - "Assign Task", - "Reassign Task", - "Pause", - "Emergency Stop" - ], - location: `Floor ${worker.floor || "-"} . Zone ${worker.zone || "-"}` - })); + task: { + status: "Ongoing", + title: worker.taskTitle ?? "No Task Assigned", + location: { + floor: worker.floor ?? 0, + zone: worker.zone ?? "N/A", + }, + planned_time_hours: worker.plannedTime ?? 0, + time_spent_hours: worker.timeSpent ?? 0, + total_tasks: worker.totalTasks ?? 0, + completed_tasks: worker.completedTasks ?? 0, + }, + actions: ["Assign Task", "Reassign Task", "Pause", "Emergency Stop"], + location: `Floor ${worker.floor || "-"} . Zone ${worker.zone || "-"}`, + }; + }) + ); setWorkers(formattedWorkers); - } - }, [allAssets]); - - - // const employee_details = [ - // { - // "employee": { - // image: "", - // "name": "John Doe", - // "employee_id": "HR-204", - // "status": "Active", - - // }, - // "task": { - // "status": "Ongoing", - // "title": "Inspecting Machine X", - // "location": { - // "floor": 4, - // "zone": "B" - // }, - // "planned_time_hours": 6, - // "time_spent_hours": 2, - // "total_tasks": 12, - // "completed_tasks": 3 - // }, - // "actions": [ - // "Assign Task", - // "Reassign Task", - // "Pause", - // "Emergency Stop" - // ], - // "location": "Floor 4 . Zone B" - // }, - // { - // "employee": { - // image: "", - // "name": "Alice Smith", - // "employee_id": "HR-205", - // "status": "Active", - - // }, - // "task": { - // "status": "Ongoing", - // "title": "Calibrating Sensor Y", - // "location": { - // "floor": 2, - // "zone": "A" - // }, - // "planned_time_hours": 4, - // "time_spent_hours": 1.5, - // "total_tasks": 10, - // "completed_tasks": 2 - // }, - // "actions": [ - // "Assign Task", - // "Reassign Task", - // "Pause", - // "Emergency Stop" - // ], - // "location": "Floor 4 . Zone B" - // }, - // { - // "employee": { - // image: "", - // "name": "Michael Lee", - // "employee_id": "HR-206", - // "status": "Active", - - // }, - // "task": { - // "status": "Ongoing", - // "title": "Testing Conveyor Belt Z", - // "location": { - // "floor": 5, - // "zone": "C" - // }, - // "planned_time_hours": 5, - // "time_spent_hours": 3, - // "total_tasks": 8, - // "completed_tasks": 5 - // }, - // "actions": [ - // "Assign Task", - // "Reassign Task", - // "Pause", - // "Emergency Stop" - // ], - // "location": "Floor 4 . Zone B" - // }, - // ] - function handleRenameWorker(newName: string) { - + }; + fetchWorkers(); } - function handleHumanClick(employee: any) { - if (employee.modelId) { - setResourceManagementId(employee.modelId); - } +}, [allAssets]); + + + +// const employee_details = [ +// { +// "employee": { +// image: "", +// "name": "John Doe", +// "employee_id": "HR-204", +// "status": "Active", + +// }, +// "task": { +// "status": "Ongoing", +// "title": "Inspecting Machine X", +// "location": { +// "floor": 4, +// "zone": "B" +// }, +// "planned_time_hours": 6, +// "time_spent_hours": 2, +// "total_tasks": 12, +// "completed_tasks": 3 +// }, +// "actions": [ +// "Assign Task", +// "Reassign Task", +// "Pause", +// "Emergency Stop" +// ], +// "location": "Floor 4 . Zone B" +// }, +// { +// "employee": { +// image: "", +// "name": "Alice Smith", +// "employee_id": "HR-205", +// "status": "Active", + +// }, +// "task": { +// "status": "Ongoing", +// "title": "Calibrating Sensor Y", +// "location": { +// "floor": 2, +// "zone": "A" +// }, +// "planned_time_hours": 4, +// "time_spent_hours": 1.5, +// "total_tasks": 10, +// "completed_tasks": 2 +// }, +// "actions": [ +// "Assign Task", +// "Reassign Task", +// "Pause", +// "Emergency Stop" +// ], +// "location": "Floor 4 . Zone B" +// }, +// { +// "employee": { +// image: "", +// "name": "Michael Lee", +// "employee_id": "HR-206", +// "status": "Active", + +// }, +// "task": { +// "status": "Ongoing", +// "title": "Testing Conveyor Belt Z", +// "location": { +// "floor": 5, +// "zone": "C" +// }, +// "planned_time_hours": 5, +// "time_spent_hours": 3, +// "total_tasks": 8, +// "completed_tasks": 5 +// }, +// "actions": [ +// "Assign Task", +// "Reassign Task", +// "Pause", +// "Emergency Stop" +// ], +// "location": "Floor 4 . Zone B" +// }, +// ] +function handleRenameWorker(newName: string) { + + +} +function handleHumanClick(employee: any) { + if (employee.modelId) { + setResourceManagementId(employee.modelId); } +} - return ( - <> - {/* + {/* */} -
- {workers.map((employee, index) => ( -
setSelectedCard(index)} - key={index} - > -
-
-
- -
-
-
- {/*
{employee.employee.name}
*/} - -
{employee.employee.employee_id}
-
+
+ {workers.map((employee, index) => ( +
setSelectedCard(index)} + key={index} + > +
+
+
+ +
+
+ {/*
{employee.employee.name}
*/} + +
{employee.employee.employee_id}
+
+
-
{ handleHumanClick(employee.employee) }}>View in Scene
-
+
{ handleHumanClick(employee.employee) }}>View in Scene
+
-
- {/*
+
+ {/*
@@ -200,17 +215,17 @@ const Hrm = () => {
*/} -
-
+
+
-
- - Planned time: -
- - {employee.task.planned_time_hours} hr +
+ + Planned time:
- {/*
+ + {employee.task.planned_time_hours} hr +
+ {/*
@@ -228,39 +243,39 @@ const Hrm = () => { {employee.task.time_spent_hours} hr
*/} -
+
-
- - Cost per hr: -
- - {employee.task.completed_tasks} +
+ + Cost per hr:
-
-
-
-
- -
-
Location:
-
-
{employee.location}
-
-
- {/* - */} - - + {employee.task.completed_tasks}
+
+
+
+ +
+
Location:
+
+
{employee.location}
+
+
+ {/* + */} + + +
- ))} -
- - ) + +
+ ))} +
+ +) } export default Hrm diff --git a/app/src/styles/layout/resourceManagement.scss b/app/src/styles/layout/resourceManagement.scss index d62afe8..5b9678b 100644 --- a/app/src/styles/layout/resourceManagement.scss +++ b/app/src/styles/layout/resourceManagement.scss @@ -117,6 +117,12 @@ border-radius: 50%; background-color: #fff; position: relative; + overflow: hidden; + .user-image{ + height: 300%; + width: 300%; + transform: translate(-26px, -12px); + } .status { border-radius: 50%; From b4b412ce144125ccf06671e77cb3bcced9155284 Mon Sep 17 00:00:00 2001 From: Jerald-Golden-B Date: Tue, 26 Aug 2025 12:02:46 +0530 Subject: [PATCH 05/34] added move and rotate tool using tranformControls --- .../properties/AssetProperties.tsx | 10 +- app/src/components/ui/Tools.tsx | 6 + .../model/eventHandlers/useEventHandlers.ts | 7 +- .../contextControls/contextControls.tsx | 425 +++++++++--------- .../selection3D/moveControls3D.tsx | 16 +- .../selection3D/rotateControls3D.tsx | 18 +- .../selection3D/selectionControls3D.tsx | 11 +- .../selection3D/transformControls3D.tsx | 380 ++++++++++++++++ .../transformControls/transformControls.tsx | 16 +- .../undoRedo3D/undoRedo3DControls.tsx | 2 +- app/src/store/builder/useUndoRedo3DStore.ts | 25 +- 11 files changed, 671 insertions(+), 245 deletions(-) create mode 100644 app/src/modules/scene/controls/selectionControls/selection3D/transformControls3D.tsx diff --git a/app/src/components/layout/sidebarRight/properties/AssetProperties.tsx b/app/src/components/layout/sidebarRight/properties/AssetProperties.tsx index a62abda..9c8b979 100644 --- a/app/src/components/layout/sidebarRight/properties/AssetProperties.tsx +++ b/app/src/components/layout/sidebarRight/properties/AssetProperties.tsx @@ -32,9 +32,9 @@ const AssetProperties: React.FC = () => { setUserData([]); }; - const handleUserDataChange = (id: number, newValue: string) => {}; + const handleUserDataChange = (id: number, newValue: string) => { }; - const handleRemoveUserData = (id: number) => {}; + const handleRemoveUserData = (id: number) => { }; const handleAnimationClick = (animation: string) => { if (selectedFloorItem) { @@ -57,14 +57,14 @@ const AssetProperties: React.FC = () => {
{objectPosition && ( {}} + onChange={() => { }} value1={parseFloat(objectPosition.x.toFixed(5))} value2={parseFloat(objectPosition.z.toFixed(5))} /> )} {objectRotation && ( {}} + onChange={() => { }} value={parseFloat(objectRotation.y.toFixed(5))} /> )} @@ -107,7 +107,7 @@ const AssetProperties: React.FC = () => { if (asset.modelUuid !== selectedFloorItem.uuid || !asset.animations) return ( i === 0 && ( -
+
Looks like there are no preset animations yet. Stay tuned for future additions!
diff --git a/app/src/components/ui/Tools.tsx b/app/src/components/ui/Tools.tsx index d5d49e4..b89f2ea 100644 --- a/app/src/components/ui/Tools.tsx +++ b/app/src/components/ui/Tools.tsx @@ -146,6 +146,12 @@ const Tools: React.FC = () => { case "draw-floor": is2D && setToolMode("Floor"); break; + case "move": + if (!is2D) setToolMode("Move-Asset"); + break; + case "rotate": + if (!is2D) setToolMode("Rotate-Asset"); + break; case "measure": setToolMode("MeasurementScale"); break; diff --git a/app/src/modules/builder/asset/models/model/eventHandlers/useEventHandlers.ts b/app/src/modules/builder/asset/models/model/eventHandlers/useEventHandlers.ts index 360c513..85b5b4b 100644 --- a/app/src/modules/builder/asset/models/model/eventHandlers/useEventHandlers.ts +++ b/app/src/modules/builder/asset/models/model/eventHandlers/useEventHandlers.ts @@ -34,7 +34,7 @@ export function useModelEventHandlers({ const { socket } = useSocketStore(); const { eventStore, productStore, assetStore, undoRedo3DStore } = useSceneContext(); const { push3D } = undoRedo3DStore(); - const { getAssetById, removeAsset } = assetStore(); + const { removeAsset } = assetStore(); const { zoneAssetId, setZoneAssetId } = useZoneAssetId(); const { resourceManagementId, setResourceManagementId } = useResourceManagementId(); const { removeEvent, getEventByModelUuid } = eventStore(); @@ -77,11 +77,12 @@ export function useModelEventHandlers({ } }, [zoneAssetId]) + useEffect(() => { if (!resourceManagementId) return if (resourceManagementId === asset.modelUuid) { - - + + handleDblClick(asset); } diff --git a/app/src/modules/scene/controls/contextControls/contextControls.tsx b/app/src/modules/scene/controls/contextControls/contextControls.tsx index 142f8d1..46801c1 100644 --- a/app/src/modules/scene/controls/contextControls/contextControls.tsx +++ b/app/src/modules/scene/controls/contextControls/contextControls.tsx @@ -1,41 +1,14 @@ import { useEffect, useRef, useState } from "react"; import { useThree } from "@react-three/fiber"; import { CameraControls, Html, ScreenSpace } from "@react-three/drei"; -import { - useContextActionStore, - useRenameModeStore, - useSelectedAssets, -} from "../../../../store/builder/store"; +import { useContextActionStore, useRenameModeStore, useSelectedAssets, useToggleView, useToolMode, } from "../../../../store/builder/store"; import ContextMenu from "../../../../components/ui/menu/contextMenu"; +import useModuleStore from "../../../../store/useModuleStore"; function ContextControls() { - const { gl, controls } = useThree(); - const [canRender, setCanRender] = useState(false); - const [visibility, setVisibility] = useState({ - rename: true, - focus: true, - flipX: true, - flipZ: true, - move: true, - rotate: true, - duplicate: true, - copy: true, - paste: true, - modifier: false, - group: false, - array: false, - delete: true, - }); - const [menuPosition, setMenuPosition] = useState({ x: 0, y: 0 }); - const { selectedAssets } = useSelectedAssets(); - const { setContextAction } = useContextActionStore(); - const { setIsRenameMode } = useRenameModeStore(); - const rightDrag = useRef(false); - const isRightMouseDown = useRef(false); - - useEffect(() => { - if (selectedAssets.length === 1) { - setVisibility({ + const { gl, controls } = useThree(); + const [canRender, setCanRender] = useState(false); + const [visibility, setVisibility] = useState({ rename: true, focus: true, flipX: true, @@ -49,195 +22,219 @@ function ContextControls() { group: false, array: false, delete: true, - }); - } else if (selectedAssets.length > 1) { - setVisibility({ - rename: false, - focus: true, - flipX: true, - flipZ: true, - move: true, - rotate: true, - duplicate: true, - copy: true, - paste: true, - modifier: false, - group: true, - array: false, - delete: true, - }); - } else { - setVisibility({ - rename: false, - focus: false, - flipX: false, - flipZ: false, - move: false, - rotate: false, - duplicate: false, - copy: false, - paste: false, - modifier: false, - group: false, - array: false, - delete: false, - }); - } - }, [selectedAssets]); + }); + const [menuPosition, setMenuPosition] = useState({ x: 0, y: 0 }); + const { toggleView } = useToggleView(); + const { activeModule } = useModuleStore(); + const { toolMode } = useToolMode(); + const { selectedAssets } = useSelectedAssets(); + const { setContextAction } = useContextActionStore(); + const { setIsRenameMode } = useRenameModeStore(); + const rightDrag = useRef(false); + const isRightMouseDown = useRef(false); - useEffect(() => { - const canvasElement = gl.domElement; - - const onPointerDown = (evt: any) => { - if (evt.button === 2) { - isRightMouseDown.current = true; - rightDrag.current = false; - } - }; - - const onPointerMove = () => { - if (isRightMouseDown.current) { - rightDrag.current = true; - } - }; - - const onPointerUp = (evt: any) => { - if (evt.button === 2) { - isRightMouseDown.current = false; - } - }; - - const handleContextClick = (event: MouseEvent) => { - event.preventDefault(); - if (rightDrag.current) return; - if (selectedAssets.length > 0) { - setMenuPosition({ - x: event.clientX - gl.domElement.width / 2, - y: event.clientY - gl.domElement.height / 2, - }); - setCanRender(true); - if (controls) { - (controls as CameraControls).enabled = false; + useEffect(() => { + if (selectedAssets.length === 1) { + setVisibility({ + rename: true, + focus: true, + flipX: true, + flipZ: true, + move: true, + rotate: true, + duplicate: true, + copy: true, + paste: true, + modifier: false, + group: false, + array: false, + delete: true, + }); + } else if (selectedAssets.length > 1) { + setVisibility({ + rename: false, + focus: true, + flipX: true, + flipZ: true, + move: true, + rotate: true, + duplicate: true, + copy: true, + paste: true, + modifier: false, + group: true, + array: false, + delete: true, + }); + } else { + setVisibility({ + rename: false, + focus: false, + flipX: false, + flipZ: false, + move: false, + rotate: false, + duplicate: false, + copy: false, + paste: false, + modifier: false, + group: false, + array: false, + delete: false, + }); } - } else { + }, [selectedAssets]); + + useEffect(() => { + const canvasElement = gl.domElement; + + const onPointerDown = (evt: any) => { + if (evt.button === 2) { + isRightMouseDown.current = true; + rightDrag.current = false; + } + }; + + const onPointerMove = () => { + if (isRightMouseDown.current) { + rightDrag.current = true; + } + }; + + const onPointerUp = (evt: any) => { + if (evt.button === 2) { + isRightMouseDown.current = false; + } + }; + + const handleContextClick = (event: MouseEvent) => { + event.preventDefault(); + if (rightDrag.current) return; + if (selectedAssets.length > 0) { + setMenuPosition({ x: event.clientX - gl.domElement.width / 2, y: event.clientY - gl.domElement.height / 2, }); + setCanRender(true); + if (controls) { + (controls as CameraControls).enabled = false; + } + } else { + setCanRender(false); + if (controls) { + (controls as CameraControls).enabled = true; + } + } + }; + + if (selectedAssets.length > 0 && !toggleView && activeModule === "builder" && toolMode === 'cursor') { + canvasElement.addEventListener("pointerdown", onPointerDown); + canvasElement.addEventListener("pointermove", onPointerMove); + canvasElement.addEventListener("pointerup", onPointerUp); + canvasElement.addEventListener("contextmenu", handleContextClick); + } else { + setCanRender(false); + if (controls) { + (controls as CameraControls).enabled = true; + } + setMenuPosition({ x: 0, y: 0 }); + } + + return () => { + canvasElement.removeEventListener("pointerdown", onPointerDown); + canvasElement.removeEventListener("pointermove", onPointerMove); + canvasElement.removeEventListener("pointerup", onPointerUp); + canvasElement.removeEventListener("contextmenu", handleContextClick); + }; + }, [controls, gl, selectedAssets, toggleView, activeModule, toolMode]); + + const handleAssetRename = () => { setCanRender(false); if (controls) { - (controls as CameraControls).enabled = true; + (controls as CameraControls).enabled = true; } - } + setContextAction("renameAsset"); + setIsRenameMode(true); + }; + const handleAssetFocus = () => { + setCanRender(false); + if (controls) { + (controls as CameraControls).enabled = true; + } + setContextAction("focusAsset"); + }; + const handleAssetMove = () => { + setCanRender(false); + if (controls) { + (controls as CameraControls).enabled = true; + } + setContextAction("moveAsset"); + }; + const handleAssetRotate = () => { + setCanRender(false); + if (controls) { + (controls as CameraControls).enabled = true; + } + setContextAction("rotateAsset"); + }; + const handleAssetCopy = () => { + setCanRender(false); + if (controls) { + (controls as CameraControls).enabled = true; + } + setContextAction("copyAsset"); + }; + const handleAssetPaste = () => { + setCanRender(false); + if (controls) { + (controls as CameraControls).enabled = true; + } + setContextAction("pasteAsset"); + }; + const handleAssetDelete = () => { + setCanRender(false); + if (controls) { + (controls as CameraControls).enabled = true; + } + setContextAction("deleteAsset"); + }; + const handleAssetDuplicate = () => { + setCanRender(false); + if (controls) { + (controls as CameraControls).enabled = true; + } + setContextAction("duplicateAsset"); }; - if (selectedAssets.length > 0) { - canvasElement.addEventListener("pointerdown", onPointerDown); - canvasElement.addEventListener("pointermove", onPointerMove); - canvasElement.addEventListener("pointerup", onPointerUp); - canvasElement.addEventListener("contextmenu", handleContextClick); - } else { - setCanRender(false); - if (controls) { - (controls as CameraControls).enabled = true; - } - setMenuPosition({ x: 0, y: 0 }); - } - - return () => { - canvasElement.removeEventListener("pointerdown", onPointerDown); - canvasElement.removeEventListener("pointermove", onPointerMove); - canvasElement.removeEventListener("pointerup", onPointerUp); - canvasElement.removeEventListener("contextmenu", handleContextClick); - }; - }, [controls, gl, selectedAssets]); - - const handleAssetRename = () => { - setCanRender(false); - if (controls) { - (controls as CameraControls).enabled = true; - } - setContextAction("renameAsset"); - setIsRenameMode(true); - }; - const handleAssetFocus = () => { - setCanRender(false); - if (controls) { - (controls as CameraControls).enabled = true; - } - setContextAction("focusAsset"); - }; - const handleAssetMove = () => { - setCanRender(false); - if (controls) { - (controls as CameraControls).enabled = true; - } - setContextAction("moveAsset"); - }; - const handleAssetRotate = () => { - setCanRender(false); - if (controls) { - (controls as CameraControls).enabled = true; - } - setContextAction("rotateAsset"); - }; - const handleAssetCopy = () => { - setCanRender(false); - if (controls) { - (controls as CameraControls).enabled = true; - } - setContextAction("copyAsset"); - }; - const handleAssetPaste = () => { - setCanRender(false); - if (controls) { - (controls as CameraControls).enabled = true; - } - setContextAction("pasteAsset"); - }; - const handleAssetDelete = () => { - setCanRender(false); - if (controls) { - (controls as CameraControls).enabled = true; - } - setContextAction("deleteAsset"); - }; - const handleAssetDuplicate = () => { - setCanRender(false); - if (controls) { - (controls as CameraControls).enabled = true; - } - setContextAction("duplicateAsset"); - }; - - return ( - <> - {canRender && ( - - - handleAssetRename()} - onFocus={() => handleAssetFocus()} - onFlipX={() => console.log("Flip to X")} - onFlipZ={() => console.log("Flip to Z")} - onMove={() => handleAssetMove()} - onRotate={() => handleAssetRotate()} - onDuplicate={() => handleAssetDuplicate()} - onCopy={() => handleAssetCopy()} - onPaste={() => handleAssetPaste()} - onGroup={() => console.log("Group")} - onArray={() => console.log("Array")} - onDelete={() => handleAssetDelete()} - /> - - - )} - - ); + return ( + <> + {canRender && ( + + + handleAssetRename()} + onFocus={() => handleAssetFocus()} + onFlipX={() => console.log("Flip to X")} + onFlipZ={() => console.log("Flip to Z")} + onMove={() => handleAssetMove()} + onRotate={() => handleAssetRotate()} + onDuplicate={() => handleAssetDuplicate()} + onCopy={() => handleAssetCopy()} + onPaste={() => handleAssetPaste()} + onGroup={() => console.log("Group")} + onArray={() => console.log("Array")} + onDelete={() => handleAssetDelete()} + /> + + + )} + + ); } export default ContextControls; diff --git a/app/src/modules/scene/controls/selectionControls/selection3D/moveControls3D.tsx b/app/src/modules/scene/controls/selectionControls/selection3D/moveControls3D.tsx index e0940c9..248ec9d 100644 --- a/app/src/modules/scene/controls/selectionControls/selection3D/moveControls3D.tsx +++ b/app/src/modules/scene/controls/selectionControls/selection3D/moveControls3D.tsx @@ -1,7 +1,7 @@ import * as THREE from "three"; import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { useFrame, useThree } from "@react-three/fiber"; -import { useContextActionStore, useSelectedAssets, useSocketStore, useToggleView, } from "../../../../../store/builder/store"; +import { useContextActionStore, useSelectedAssets, useSocketStore, useToggleView, useToolMode, } from "../../../../../store/builder/store"; import * as Types from "../../../../../types/world/worldTypes"; import { detectModifierKeys } from "../../../../../utils/shortcutkeys/detectModifierKeys"; import { upsertProductOrEventApi } from "../../../../../services/simulation/products/UpsertProductOrEventApi"; @@ -12,6 +12,7 @@ import { useProductContext } from "../../../../simulation/products/productContex import { getUserData } from "../../../../../functions/getUserData"; import { useSceneContext } from "../../../sceneContext"; import { useVersionContext } from "../../../../builder/version/versionContext"; +import useModuleStore from "../../../../../store/useModuleStore"; // import { setAssetsApi } from '../../../../../services/factoryBuilder/asset/floorAsset/setAssetsApi'; @@ -20,6 +21,8 @@ function MoveControls3D({ boundingBoxRef }: any) { const plane = useMemo(() => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0), []); const { toggleView } = useToggleView(); + const { toolMode } = useToolMode(); + const { activeModule } = useModuleStore(); const { selectedAssets, setSelectedAssets } = useSelectedAssets(); const { selectedProductStore } = useProductContext(); const { selectedProduct } = selectedProductStore(); @@ -162,7 +165,7 @@ function MoveControls3D({ boundingBoxRef }: any) { } }; - if (!toggleView) { + if (!toggleView && toolMode === 'cursor') { canvasElement.addEventListener("pointerdown", onPointerDown); canvasElement.addEventListener("pointermove", onPointerMove); canvasElement.addEventListener("pointerup", onPointerUp); @@ -178,7 +181,14 @@ function MoveControls3D({ boundingBoxRef }: any) { canvasElement?.removeEventListener("keyup", onKeyUp); }; // eslint-disable-next-line react-hooks/exhaustive-deps - }, [camera, controls, scene, toggleView, selectedAssets, socket, pastedObjects, duplicatedObjects, movedObjects, rotatedObjects, keyEvent, initialStates]); + }, [camera, controls, scene, toggleView, toolMode, selectedAssets, socket, pastedObjects, duplicatedObjects, movedObjects, rotatedObjects, keyEvent, initialStates]); + + useEffect(() => { + if (activeModule !== "builder" || toolMode !== 'cursor' || toggleView) { + resetToInitialPositions(); + setMovedObjects([]); + } + }, [activeModule, toolMode, toggleView]); const calculateDragOffset = useCallback((point: THREE.Object3D, hitPoint: THREE.Vector3) => { const pointPosition = new THREE.Vector3().copy(point.position); diff --git a/app/src/modules/scene/controls/selectionControls/selection3D/rotateControls3D.tsx b/app/src/modules/scene/controls/selectionControls/selection3D/rotateControls3D.tsx index 5af7a3e..66904b2 100644 --- a/app/src/modules/scene/controls/selectionControls/selection3D/rotateControls3D.tsx +++ b/app/src/modules/scene/controls/selectionControls/selection3D/rotateControls3D.tsx @@ -1,7 +1,7 @@ import * as THREE from "three"; import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { useFrame, useThree } from "@react-three/fiber"; -import { useContextActionStore, useSelectedAssets, useSocketStore, useToggleView } from "../../../../../store/builder/store"; +import { useContextActionStore, useSelectedAssets, useSocketStore, useToggleView, useToolMode } from "../../../../../store/builder/store"; import * as Types from "../../../../../types/world/worldTypes"; import { upsertProductOrEventApi } from "../../../../../services/simulation/products/UpsertProductOrEventApi"; import { useParams } from "react-router-dom"; @@ -11,6 +11,7 @@ import { useSceneContext } from "../../../sceneContext"; import { useVersionContext } from "../../../../builder/version/versionContext"; import { detectModifierKeys } from "../../../../../utils/shortcutkeys/detectModifierKeys"; import { handleAssetRotationSnap } from "./functions/handleAssetRotationSnap"; +import useModuleStore from "../../../../../store/useModuleStore"; // import { setAssetsApi } from '../../../../../services/factoryBuilder/asset/floorAsset/setAssetsApi'; @@ -18,6 +19,8 @@ function RotateControls3D() { const { camera, gl, scene, pointer, raycaster } = useThree(); const { toggleView } = useToggleView(); + const { toolMode } = useToolMode(); + const { activeModule } = useModuleStore(); const { selectedAssets, setSelectedAssets } = useSelectedAssets(); const { selectedProductStore } = useProductContext(); const { selectedProduct } = selectedProductStore(); @@ -137,7 +140,7 @@ function RotateControls3D() { prevPointerPosition.current = currentPointer.clone(); }; - if (!toggleView) { + if (!toggleView && toolMode === 'cursor') { canvasElement.addEventListener("pointerdown", onPointerDown); canvasElement.addEventListener("pointermove", onPointerMove); canvasElement.addEventListener("pointerup", onPointerUp); @@ -152,7 +155,14 @@ function RotateControls3D() { canvasElement.removeEventListener("keydown", onKeyDown); canvasElement?.removeEventListener("keyup", onKeyUp); }; - }, [camera, scene, toggleView, selectedAssets, rotatedObjects, pastedObjects, duplicatedObjects, movedObjects, keyEvent, initialPositions, initialRotations]); + }, [camera, scene, toggleView, toolMode, selectedAssets, rotatedObjects, pastedObjects, duplicatedObjects, movedObjects, keyEvent, initialPositions, initialRotations]); + + useEffect(() => { + if (activeModule !== "builder" || toolMode !== 'cursor' || toggleView) { + resetToInitialRotations(); + setRotatedObjects([]); + } + }, [activeModule, toolMode, toggleView]); const resetToInitialRotations = useCallback(() => { setTimeout(() => { @@ -385,7 +395,7 @@ function RotateControls3D() { setIsRotating(false); clearSelection(); - }, [rotatedObjects, eventStore, productStore, selectedProduct, updateBackend, projectId, updateAsset, organization, socket, selectedVersion, userId]); + }, [rotatedObjects, eventStore, productStore, selectedProduct, updateBackend, projectId, updateAsset, organization, socket, selectedVersion, userId, initialPositions, initialRotations]); const clearSelection = () => { setPastedObjects([]); diff --git a/app/src/modules/scene/controls/selectionControls/selection3D/selectionControls3D.tsx b/app/src/modules/scene/controls/selectionControls/selection3D/selectionControls3D.tsx index ed458c2..512b80f 100644 --- a/app/src/modules/scene/controls/selectionControls/selection3D/selectionControls3D.tsx +++ b/app/src/modules/scene/controls/selectionControls/selection3D/selectionControls3D.tsx @@ -16,22 +16,23 @@ import DuplicationControls3D from "./duplicationControls3D"; import CopyPasteControls3D from "./copyPasteControls3D"; import MoveControls3D from "./moveControls3D"; import RotateControls3D from "./rotateControls3D"; +import TransformControls3D from "./transformControls3D"; // import { deleteFloorItem } from '../../../../../services/factoryBuilder/asset/floorAsset/deleteFloorItemApi'; const SelectionControls3D: React.FC = () => { const { camera, controls, gl, scene, raycaster, pointer } = useThree(); const { toggleView } = useToggleView(); + const { activeModule } = useModuleStore(); + const { toolMode } = useToolMode(); const { selectedAssets, setSelectedAssets } = useSelectedAssets(); const boundingBoxRef = useRef(); - const { activeModule } = useModuleStore(); const { socket } = useSocketStore(); const { contextAction, setContextAction } = useContextActionStore() const { assetStore, eventStore, productStore, undoRedo3DStore } = useSceneContext(); const { push3D } = undoRedo3DStore(); const { removeAsset, getAssetById, movedObjects, rotatedObjects, copiedObjects, pastedObjects, duplicatedObjects, setPastedObjects, setDuplicatedObjects } = assetStore(); const selectionBox = useMemo(() => new SelectionBox(camera, scene), [camera, scene]); - const { toolMode } = useToolMode(); const { selectedVersionStore } = useVersionContext(); const { selectedVersion } = selectedVersionStore(); const { selectedProductStore } = useProductContext(); @@ -202,7 +203,7 @@ const SelectionControls3D: React.FC = () => { rightClickMoved.current = false; }; - if (!toggleView && activeModule === "builder" && toolMode === 'cursor') { + if (!toggleView && activeModule === "builder" && (toolMode === 'cursor' || toolMode === 'Move-Asset' || toolMode === 'Rotate-Asset')) { helper.enabled = true; canvasElement.addEventListener("pointermove", onPointerMove); canvasElement.addEventListener("pointerup", onPointerUp); @@ -227,7 +228,7 @@ const SelectionControls3D: React.FC = () => { }, [camera, controls, scene, toggleView, selectedAssets, copiedObjects, pastedObjects, duplicatedObjects, movedObjects, socket, rotatedObjects, activeModule, toolMode]); useEffect(() => { - if (activeModule !== "builder" || toolMode !== 'cursor' || toggleView) { + if (activeModule !== "builder" || (toolMode !== 'cursor' && toolMode !== 'Move-Asset' && toolMode !== 'Rotate-Asset') || toggleView) { clearSelection(); } // eslint-disable-next-line react-hooks/exhaustive-deps @@ -381,6 +382,8 @@ const SelectionControls3D: React.FC = () => { + + ); }; diff --git a/app/src/modules/scene/controls/selectionControls/selection3D/transformControls3D.tsx b/app/src/modules/scene/controls/selectionControls/selection3D/transformControls3D.tsx new file mode 100644 index 0000000..a6aa2bb --- /dev/null +++ b/app/src/modules/scene/controls/selectionControls/selection3D/transformControls3D.tsx @@ -0,0 +1,380 @@ +import * as THREE from 'three'; +import { useRef, useEffect, useState, useCallback } from 'react'; +import { useThree } from '@react-three/fiber'; +import { TransformControls } from '@react-three/drei'; +import { useSelectedAssets, useSocketStore, useToolMode } from '../../../../../store/builder/store'; +import { useProductContext } from '../../../../simulation/products/productContext'; +import { useVersionContext } from '../../../../builder/version/versionContext'; +import { useSceneContext } from '../../../sceneContext'; +import { useParams } from 'react-router-dom'; + +import { getUserData } from '../../../../../functions/getUserData'; +import { upsertProductOrEventApi } from '../../../../../services/simulation/products/UpsertProductOrEventApi'; +import { detectModifierKeys } from '../../../../../utils/shortcutkeys/detectModifierKeys'; +// import { setAssetsApi } from '../../../../../services/factoryBuilder/asset/floorAsset/setAssetsApi'; + +function TransformControls3D() { + const { selectedAssets, setSelectedAssets } = useSelectedAssets(); + const { toolMode } = useToolMode(); + const { camera, scene, gl } = useThree(); + const transformControlsRef = useRef(null); + const [visible, setVisible] = useState(false); + const [keyEvent, setKeyEvent] = useState<"Ctrl" | "Shift" | "Ctrl+Shift" | "">(""); + const { socket } = useSocketStore(); + const { selectedProductStore } = useProductContext(); + const { selectedProduct } = selectedProductStore(); + const { assetStore, eventStore, productStore, undoRedo3DStore } = useSceneContext(); + const { push3D, subscribeUndoRedo } = undoRedo3DStore(); + const { updateAsset, getAssetById } = assetStore(); + const { userId, organization } = getUserData(); + const { selectedVersionStore } = useVersionContext(); + const { selectedVersion } = selectedVersionStore(); + const { projectId } = useParams(); + const pivotPointRef = useRef(new THREE.Vector3()); + const initialPositionsRef = useRef>(new Map()); + const initialRotationsRef = useRef>(new Map()); + const initialPivotPositionRef = useRef(new THREE.Vector3()); + const initialQuaternionsRef = useRef>(new Map()); + const tempObjectRef = useRef(new THREE.Object3D()); + + const updateBackend = useCallback(( + productName: string, + productUuid: string, + projectId: string, + eventData: any + ) => { + upsertProductOrEventApi({ + productName: productName, + productUuid: productUuid, + projectId: projectId, + eventDatas: eventData, + versionId: selectedVersion?.versionId || '', + }); + }, [selectedVersion]); + + const recalcGizmo = useCallback(() => { + if (selectedAssets.length === 0) return; + + const bbox = new THREE.Box3(); + selectedAssets.forEach(obj => bbox.expandByObject(obj)); + bbox.getCenter(pivotPointRef.current); + initialPivotPositionRef.current.copy(pivotPointRef.current); + + tempObjectRef.current.position.copy(pivotPointRef.current); + tempObjectRef.current.rotation.set(0, 0, 0); + tempObjectRef.current.scale.set(1, 1, 1); + + initialPositionsRef.current.clear(); + initialRotationsRef.current.clear(); + initialQuaternionsRef.current.clear(); + + selectedAssets.forEach(obj => { + initialPositionsRef.current.set(obj.uuid, obj.position.clone()); + initialRotationsRef.current.set(obj.uuid, obj.rotation.clone()); + initialQuaternionsRef.current.set(obj.uuid, obj.quaternion.clone()); + }); + }, [selectedAssets]); + + useEffect(() => { + const unsubscribe = subscribeUndoRedo(() => { + setTimeout(() => { + recalcGizmo(); + }, 10); + }); + return unsubscribe; + }, [subscribeUndoRedo, recalcGizmo]); + + const handleTransformationComplete = useCallback(() => { + if (selectedAssets.length === 0) return; + + const updatedAssets = [...selectedAssets]; + setSelectedAssets(updatedAssets); + + selectedAssets.forEach(obj => { + const asset = getAssetById(obj.uuid); + if (!asset) return; + + updateAsset(asset.modelUuid, { + position: [obj.position.x, obj.position.y, obj.position.z], + rotation: [obj.rotation.x, obj.rotation.y, obj.rotation.z], + }); + + if (asset.eventData) { + const eventData = eventStore.getState().getEventByModelUuid(asset.modelUuid); + const productData = productStore.getState().getEventByModelUuid( + selectedProduct.productUuid, + asset.modelUuid + ); + + if (eventData) { + eventStore.getState().updateEvent(asset.modelUuid, { + position: [obj.position.x, obj.position.y, obj.position.z], + rotation: [obj.rotation.x, obj.rotation.y, obj.rotation.z], + }); + } + + if (productData) { + const event = productStore + .getState() + .updateEvent( + selectedProduct.productUuid, + asset.modelUuid, + { + position: [obj.position.x, obj.position.y, obj.position.z], + rotation: [obj.rotation.x, obj.rotation.y, obj.rotation.z], + } + ); + + if (event) { + updateBackend( + selectedProduct.productName, + selectedProduct.productUuid, + projectId || '', + event + ); + } + } + } + + //REST + + // setAssetsApi( + // organization, + // asset.modelUuid, + // asset.modelName, + // [obj.position.x, 0, obj.position.z], + // { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z }, + // asset.assetId, + // false, + // true, + // ); + + //SOCKET + + const data = { + organization, + modelUuid: asset.modelUuid, + modelName: asset.modelName, + assetId: asset.assetId, + position: [obj.position.x, obj.position.y, obj.position.z], + rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z }, + isLocked: false, + isVisible: true, + socketId: socket.id, + versionId: selectedVersion?.versionId || '', + userId, + projectId + }; + + socket.emit("v1:model-asset:add", data); + + }); + }, [selectedAssets, setSelectedAssets, getAssetById, updateAsset, eventStore, productStore, selectedProduct, updateBackend, projectId, organization, socket, selectedVersion, userId, push3D]); + + useEffect(() => { + const temp = tempObjectRef.current; + scene.add(temp); + return () => { + scene.remove(temp); + }; + }, [scene]); + + useEffect(() => { + if (selectedAssets.length === 0) { + setVisible(false); + return; + } + + const bbox = new THREE.Box3(); + selectedAssets.forEach(obj => bbox.expandByObject(obj)); + bbox.getCenter(pivotPointRef.current); + initialPivotPositionRef.current.copy(pivotPointRef.current); + + tempObjectRef.current.position.copy(pivotPointRef.current); + tempObjectRef.current.rotation.set(0, 0, 0); + tempObjectRef.current.scale.set(1, 1, 1); + + initialPositionsRef.current.clear(); + initialRotationsRef.current.clear(); + initialQuaternionsRef.current.clear(); + selectedAssets.forEach(obj => { + initialPositionsRef.current.set(obj.uuid, obj.position.clone()); + initialRotationsRef.current.set(obj.uuid, obj.rotation.clone()); + initialQuaternionsRef.current.set(obj.uuid, obj.quaternion.clone()); + }); + + setVisible(true); + }, [selectedAssets]); + + useEffect(() => { + const controls = transformControlsRef.current; + if (!controls || !visible) return; + + controls.attach(tempObjectRef.current); + controls.setMode(toolMode === 'Move-Asset' ? 'translate' : 'rotate'); + + const onObjectChange = () => { + const temp = tempObjectRef.current; + if (!temp) return; + + selectedAssets.forEach(obj => { + const initialPos = initialPositionsRef.current.get(obj.uuid); + const initialQuat = initialQuaternionsRef.current.get(obj.uuid); + if (!initialPos || !initialQuat) return; + + if (controls.mode === 'translate') { + const delta = new THREE.Vector3().copy(temp.position).sub(initialPivotPositionRef.current); + obj.position.copy(initialPos).add(delta); + } else if (controls.mode === 'rotate') { + const deltaQuat = new THREE.Quaternion().setFromEuler(temp.rotation); + const relPos = initialPos.clone().sub(initialPivotPositionRef.current); + relPos.applyQuaternion(deltaQuat); + obj.position.copy(initialPivotPositionRef.current).add(relPos); + obj.quaternion.copy(initialQuat).multiply(deltaQuat); + } + }); + }; + + const onMouseUp = () => { + const assetsToUpdate: AssetData[] = []; + const undoActions: UndoRedo3DAction[] = []; + + selectedAssets.forEach((obj) => { + const asset = getAssetById(obj.uuid); + if (!asset) return; + + const initialPos = initialPositionsRef.current.get(obj.uuid); + const initialRot = initialRotationsRef.current.get(obj.uuid); + if (!initialPos || !initialRot) return; + + const assetData: Asset = { + ...asset, + position: [initialPos.x, initialPos.y, initialPos.z], + rotation: [initialRot.x, initialRot.y, initialRot.z], + }; + + const newData: Asset = { + ...asset, + position: [obj.position.x, obj.position.y, obj.position.z], + rotation: [obj.rotation.x, obj.rotation.y, obj.rotation.z], + }; + + assetsToUpdate.push({ + type: "Asset", + assetData, + newData, + timeStap: new Date().toISOString(), + }); + + updateAsset(asset.modelUuid, { + position: [obj.position.x, obj.position.y, obj.position.z], + rotation: [obj.rotation.x, obj.rotation.y, obj.rotation.z], + }); + }); + + if (assetsToUpdate.length > 0) { + if (assetsToUpdate.length === 1) { + undoActions.push({ + module: "builder", + actionType: "Asset-Update", + asset: assetsToUpdate[0], + }); + } else { + undoActions.push({ + module: "builder", + actionType: "Assets-Update", + assets: assetsToUpdate, + }); + } + + push3D({ + type: "Scene", + actions: undoActions, + }); + } + + selectedAssets.forEach(obj => { + initialPositionsRef.current.set(obj.uuid, obj.position.clone()); + initialRotationsRef.current.set(obj.uuid, obj.rotation.clone()); + initialQuaternionsRef.current.set(obj.uuid, obj.quaternion.clone()); + }); + + if (selectedAssets.length > 0) { + const bbox = new THREE.Box3(); + selectedAssets.forEach(obj => bbox.expandByObject(obj)); + bbox.getCenter(pivotPointRef.current); + initialPivotPositionRef.current.copy(pivotPointRef.current); + + tempObjectRef.current.position.copy(pivotPointRef.current); + tempObjectRef.current.rotation.set(0, 0, 0); + } + + recalcGizmo(); + handleTransformationComplete(); + }; + + controls.addEventListener('objectChange', onObjectChange); + controls.addEventListener('mouseUp', onMouseUp); + + return () => { + controls.removeEventListener('objectChange', onObjectChange); + controls.removeEventListener('mouseUp', onMouseUp); + controls.detach(); + }; + }, [selectedAssets, toolMode, visible, handleTransformationComplete]); + + useEffect(() => { + const canvasElement = gl.domElement; + + const handleKeyDown = (event: KeyboardEvent) => { + const keyCombination = detectModifierKeys(event); + + if (keyCombination !== keyEvent) { + if (keyCombination === "Ctrl" || keyCombination === "Ctrl+Shift" || keyCombination === "Shift") { + setKeyEvent(keyCombination); + } else { + setKeyEvent(""); + } + } + }; + + const onKeyUp = (event: KeyboardEvent) => { + const keyCombination = detectModifierKeys(event); + + if (keyCombination === "") { + setKeyEvent(""); + } else if (keyCombination === "Ctrl" || keyCombination === "Ctrl+Shift" || keyCombination === "Shift") { + setKeyEvent(keyCombination); + } + } + + if (visible) { + canvasElement.addEventListener('keydown', handleKeyDown); + canvasElement.addEventListener('keyup', onKeyUp); + } + + return () => { + canvasElement.removeEventListener('keydown', handleKeyDown); + canvasElement.removeEventListener('keyup', onKeyUp); + } + }, [gl, visible, keyEvent]); + + if (!visible || (toolMode !== 'Move-Asset' && toolMode !== 'Rotate-Asset')) { + return null; + } + + return ( + + ); +} + +export default TransformControls3D; \ No newline at end of file diff --git a/app/src/modules/scene/controls/transformControls/transformControls.tsx b/app/src/modules/scene/controls/transformControls/transformControls.tsx index 415be9b..8428a63 100644 --- a/app/src/modules/scene/controls/transformControls/transformControls.tsx +++ b/app/src/modules/scene/controls/transformControls/transformControls.tsx @@ -1,17 +1,17 @@ -import { TransformControls } from "@react-three/drei"; import * as THREE from "three"; -import { useSelectedFloorItem, useObjectPosition, useObjectRotation, useActiveTool, useSocketStore } from "../../../../store/builder/store"; -import { useThree } from "@react-three/fiber"; - -import { useEffect, useRef, useState } from "react"; -import { detectModifierKeys } from "../../../../utils/shortcutkeys/detectModifierKeys"; -import { upsertProductOrEventApi } from "../../../../services/simulation/products/UpsertProductOrEventApi"; -// import { setAssetsApi } from "../../../../services/factoryBuilder/asset/floorAsset/setAssetsApi"; import { useParams } from "react-router-dom"; +import { useThree } from "@react-three/fiber"; +import { TransformControls } from "@react-three/drei"; +import { useEffect, useRef, useState } from "react"; import { useProductContext } from "../../../simulation/products/productContext"; import { getUserData } from "../../../../functions/getUserData"; import { useSceneContext } from "../../sceneContext"; import { useVersionContext } from "../../../builder/version/versionContext"; +import { useSelectedFloorItem, useObjectPosition, useObjectRotation, useActiveTool, useSocketStore } from "../../../../store/builder/store"; + +import { detectModifierKeys } from "../../../../utils/shortcutkeys/detectModifierKeys"; +import { upsertProductOrEventApi } from "../../../../services/simulation/products/UpsertProductOrEventApi"; +// import { setAssetsApi } from "../../../../services/factoryBuilder/asset/floorAsset/setAssetsApi"; export default function TransformControl() { const state = useThree(); diff --git a/app/src/modules/scene/controls/undoRedoControls/undoRedo3D/undoRedo3DControls.tsx b/app/src/modules/scene/controls/undoRedoControls/undoRedo3D/undoRedo3DControls.tsx index 3ca5633..9331320 100644 --- a/app/src/modules/scene/controls/undoRedoControls/undoRedo3D/undoRedo3DControls.tsx +++ b/app/src/modules/scene/controls/undoRedoControls/undoRedo3D/undoRedo3DControls.tsx @@ -20,7 +20,7 @@ function UndoRedo3DControls() { const { selectedVersion } = selectedVersionStore(); useEffect(() => { - // console.log(undoStack, redoStack); + console.log(undoStack, redoStack); }, [undoStack, redoStack]); useEffect(() => { diff --git a/app/src/store/builder/useUndoRedo3DStore.ts b/app/src/store/builder/useUndoRedo3DStore.ts index e8c3ce3..d3e2b7b 100644 --- a/app/src/store/builder/useUndoRedo3DStore.ts +++ b/app/src/store/builder/useUndoRedo3DStore.ts @@ -13,6 +13,9 @@ type UndoRedo3DStore = { peekUndo3D: () => UndoRedo3DTypes | undefined; peekRedo3D: () => UndoRedo3DTypes | undefined; + + subscribeUndoRedo: (cb: (action: { type: "undo" | "redo"; entry: UndoRedo3DTypes }) => void) => () => void; + _listeners: ((action: { type: "undo" | "redo"; entry: UndoRedo3DTypes }) => void)[]; }; export const createUndoRedo3DStore = () => { @@ -20,15 +23,14 @@ export const createUndoRedo3DStore = () => { immer((set, get) => ({ undoStack: [], redoStack: [], + _listeners: [], push3D: (entry) => { set((state) => { state.undoStack.push(entry); - if (state.undoStack.length > undoRedoConfig.undoRedoCount) { state.undoStack.shift(); } - state.redoStack = []; }); }, @@ -41,6 +43,9 @@ export const createUndoRedo3DStore = () => { state.redoStack.unshift(lastAction); } }); + if (lastAction) { + get()._listeners.forEach((cb) => cb({ type: "undo", entry: lastAction! })); + } return lastAction; }, @@ -52,6 +57,9 @@ export const createUndoRedo3DStore = () => { state.undoStack.push(redoAction); } }); + if (redoAction) { + get()._listeners.forEach((cb) => cb({ type: "redo", entry: redoAction! })); + } return redoAction; }, @@ -71,8 +79,19 @@ export const createUndoRedo3DStore = () => { const stack = get().redoStack; return stack.length > 0 ? stack[0] : undefined; }, + + subscribeUndoRedo: (cb) => { + set((state) => { + state._listeners.push(cb); + }); + return () => { + set((state) => { + state._listeners = state._listeners.filter((l) => l !== cb); + }); + }; + }, })) ); -} +}; export type UndoRedo3DStoreType = ReturnType; \ No newline at end of file From 06b6b3d0cec4b056d9878514eb26742d2d813c06 Mon Sep 17 00:00:00 2001 From: Jerald-Golden-B Date: Tue, 26 Aug 2025 12:31:27 +0530 Subject: [PATCH 06/34] new TransformControls3D bug fix --- .../selection3D/rotateControls3D.tsx | 19 ++++++++++++++----- .../selection3D/transformControls3D.tsx | 4 ++-- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/app/src/modules/scene/controls/selectionControls/selection3D/rotateControls3D.tsx b/app/src/modules/scene/controls/selectionControls/selection3D/rotateControls3D.tsx index 66904b2..c4a8d19 100644 --- a/app/src/modules/scene/controls/selectionControls/selection3D/rotateControls3D.tsx +++ b/app/src/modules/scene/controls/selectionControls/selection3D/rotateControls3D.tsx @@ -37,6 +37,7 @@ function RotateControls3D() { const [initialRotations, setInitialRotations] = useState>({}); const [initialPositions, setInitialPositions] = useState>({}); const [isRotating, setIsRotating] = useState(false); + const [isIndividualRotating, setIsIndividualRotating] = useState(false); const prevPointerPosition = useRef(null); const rotationCenter = useRef(null); const mouseButtonsDown = useRef<{ left: boolean; right: boolean }>({ left: false, right: false, }); @@ -120,6 +121,12 @@ function RotateControls3D() { } } + if (event.key.toLowerCase() === 'i') { + if (rotatedObjects.length > 0) { + setIsIndividualRotating(!isIndividualRotating); + } + } + if (event.key.toLowerCase() === "escape") { event.preventDefault(); resetToInitialRotations(); @@ -155,7 +162,7 @@ function RotateControls3D() { canvasElement.removeEventListener("keydown", onKeyDown); canvasElement?.removeEventListener("keyup", onKeyUp); }; - }, [camera, scene, toggleView, toolMode, selectedAssets, rotatedObjects, pastedObjects, duplicatedObjects, movedObjects, keyEvent, initialPositions, initialRotations]); + }, [camera, scene, toggleView, toolMode, selectedAssets, rotatedObjects, pastedObjects, duplicatedObjects, movedObjects, keyEvent, initialPositions, initialRotations, isIndividualRotating]); useEffect(() => { if (activeModule !== "builder" || toolMode !== 'cursor' || toggleView) { @@ -214,10 +221,12 @@ function RotateControls3D() { wasShiftHeldRef }); - const relativePosition = new THREE.Vector3().subVectors(obj.position, center); - const rotationMatrix = new THREE.Matrix4().makeRotationY(angleDelta); - relativePosition.applyMatrix4(rotationMatrix); - obj.position.copy(center).add(relativePosition); + if (!isIndividualRotating) { + const relativePosition = new THREE.Vector3().subVectors(obj.position, center); + const rotationMatrix = new THREE.Matrix4().makeRotationY(angleDelta); + relativePosition.applyMatrix4(rotationMatrix); + obj.position.copy(center).add(relativePosition); + } const rotationQuat = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0, 1, 0), angleDelta); obj.quaternion.multiply(rotationQuat); diff --git a/app/src/modules/scene/controls/selectionControls/selection3D/transformControls3D.tsx b/app/src/modules/scene/controls/selectionControls/selection3D/transformControls3D.tsx index a6aa2bb..3f3162b 100644 --- a/app/src/modules/scene/controls/selectionControls/selection3D/transformControls3D.tsx +++ b/app/src/modules/scene/controls/selectionControls/selection3D/transformControls3D.tsx @@ -226,11 +226,11 @@ function TransformControls3D() { const delta = new THREE.Vector3().copy(temp.position).sub(initialPivotPositionRef.current); obj.position.copy(initialPos).add(delta); } else if (controls.mode === 'rotate') { - const deltaQuat = new THREE.Quaternion().setFromEuler(temp.rotation); + const deltaQuat = temp.quaternion.clone(); const relPos = initialPos.clone().sub(initialPivotPositionRef.current); relPos.applyQuaternion(deltaQuat); obj.position.copy(initialPivotPositionRef.current).add(relPos); - obj.quaternion.copy(initialQuat).multiply(deltaQuat); + obj.quaternion.copy(deltaQuat).multiply(initialQuat); } }); }; From bb2a27e2f9ecc37625d019357aedaf75a9df02e0 Mon Sep 17 00:00:00 2001 From: Jerald-Golden-B Date: Tue, 26 Aug 2025 12:33:31 +0530 Subject: [PATCH 07/34] added individual rotation to rotation controls --- .../controls/selectionControls/selection3D/rotateControls3D.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/modules/scene/controls/selectionControls/selection3D/rotateControls3D.tsx b/app/src/modules/scene/controls/selectionControls/selection3D/rotateControls3D.tsx index c4a8d19..4f2319f 100644 --- a/app/src/modules/scene/controls/selectionControls/selection3D/rotateControls3D.tsx +++ b/app/src/modules/scene/controls/selectionControls/selection3D/rotateControls3D.tsx @@ -403,6 +403,7 @@ function RotateControls3D() { } setIsRotating(false); + setIsIndividualRotating(false); clearSelection(); }, [rotatedObjects, eventStore, productStore, selectedProduct, updateBackend, projectId, updateAsset, organization, socket, selectedVersion, userId, initialPositions, initialRotations]); @@ -412,6 +413,7 @@ function RotateControls3D() { setMovedObjects([]); setRotatedObjects([]); setSelectedAssets([]); + setIsIndividualRotating(false); }; return null; From 0387d7a932d3a474b04f376858303e6f610eaf17 Mon Sep 17 00:00:00 2001 From: Jerald-Golden-B Date: Tue, 26 Aug 2025 13:15:53 +0530 Subject: [PATCH 08/34] file and folder structure changed --- app/src/modules/scene/camera/camMode.tsx | 9 ++- .../{ => functions}/firstPersonCamera.ts | 78 +++++++++---------- .../{ => functions}/switchToFirstPerson.ts | 48 ++++++------ .../{ => functions}/switchToThirdPerson.ts | 56 ++++++------- .../{ => functions}/updateCameraPosition.ts | 52 ++++++------- .../cameraShortcutsControls.tsx} | 37 ++++++--- app/src/modules/scene/controls/controls.tsx | 21 ++--- 7 files changed, 161 insertions(+), 140 deletions(-) rename app/src/modules/scene/camera/{ => functions}/firstPersonCamera.ts (92%) rename app/src/modules/scene/camera/{ => functions}/switchToFirstPerson.ts (91%) rename app/src/modules/scene/camera/{ => functions}/switchToThirdPerson.ts (93%) rename app/src/modules/scene/camera/{ => functions}/updateCameraPosition.ts (88%) rename app/src/{hooks/useCameraShortcuts.ts => modules/scene/camera/shortcutsControls/cameraShortcutsControls.tsx} (50%) diff --git a/app/src/modules/scene/camera/camMode.tsx b/app/src/modules/scene/camera/camMode.tsx index 77e2f31..60d0798 100644 --- a/app/src/modules/scene/camera/camMode.tsx +++ b/app/src/modules/scene/camera/camMode.tsx @@ -1,12 +1,13 @@ import { useFrame, useThree } from "@react-three/fiber"; import React, { useEffect, useState } from "react"; +import { useKeyboardControls } from "@react-three/drei"; import * as CONSTANTS from "../../../types/world/worldConstants"; import { useCamMode, useToggleView } from "../../../store/builder/store"; -import { useKeyboardControls } from "@react-three/drei"; -import switchToThirdPerson from "./switchToThirdPerson"; -import switchToFirstPerson from "./switchToFirstPerson"; + +import switchToThirdPerson from "./functions/switchToThirdPerson"; +import switchToFirstPerson from "./functions/switchToFirstPerson"; import { detectModifierKeys } from "../../../utils/shortcutkeys/detectModifierKeys"; -import { firstPersonCamera } from "./firstPersonCamera"; +import { firstPersonCamera } from "./functions/firstPersonCamera"; const CamMode: React.FC = () => { const { camMode, setCamMode } = useCamMode(); diff --git a/app/src/modules/scene/camera/firstPersonCamera.ts b/app/src/modules/scene/camera/functions/firstPersonCamera.ts similarity index 92% rename from app/src/modules/scene/camera/firstPersonCamera.ts rename to app/src/modules/scene/camera/functions/firstPersonCamera.ts index 85aacb3..27620f3 100644 --- a/app/src/modules/scene/camera/firstPersonCamera.ts +++ b/app/src/modules/scene/camera/functions/firstPersonCamera.ts @@ -1,39 +1,39 @@ -import * as CONSTANTS from "../../../types/world/worldConstants"; - -interface FirstPersonCameraProps { - setIsTransitioning?: (value: boolean) => void; - state: any; -} - -interface FirstPersonCameraParams extends FirstPersonCameraProps { - camMode: string; - setCamMode: (mode: string) => void; - switchToFirstPerson: (controls: any, camera: any) => Promise; - switchToThirdPerson: (controls: any, camera: any) => Promise; -} - -export async function firstPersonCamera({ - setIsTransitioning, - state, - camMode, - setCamMode, - switchToFirstPerson, - switchToThirdPerson -}: FirstPersonCameraParams): Promise { - setIsTransitioning && setIsTransitioning(true); - - state.controls.mouseButtons.left = CONSTANTS.controlsTransition.leftMouse; - state.controls.mouseButtons.right = CONSTANTS.controlsTransition.rightMouse; - state.controls.mouseButtons.wheel = CONSTANTS.controlsTransition.wheelMouse; - state.controls.mouseButtons.middle = CONSTANTS.controlsTransition.middleMouse; - - if (camMode === "ThirdPerson") { - setCamMode("FirstPerson"); - await switchToFirstPerson(state.controls, state.camera); - } else if (camMode === "FirstPerson") { - setCamMode("ThirdPerson"); - await switchToThirdPerson(state.controls, state.camera); - } - - setIsTransitioning && setIsTransitioning(false); -} +import * as CONSTANTS from "../../../../types/world/worldConstants"; + +interface FirstPersonCameraProps { + setIsTransitioning?: (value: boolean) => void; + state: any; +} + +interface FirstPersonCameraParams extends FirstPersonCameraProps { + camMode: string; + setCamMode: (mode: string) => void; + switchToFirstPerson: (controls: any, camera: any) => Promise; + switchToThirdPerson: (controls: any, camera: any) => Promise; +} + +export async function firstPersonCamera({ + setIsTransitioning, + state, + camMode, + setCamMode, + switchToFirstPerson, + switchToThirdPerson +}: FirstPersonCameraParams): Promise { + setIsTransitioning && setIsTransitioning(true); + + state.controls.mouseButtons.left = CONSTANTS.controlsTransition.leftMouse; + state.controls.mouseButtons.right = CONSTANTS.controlsTransition.rightMouse; + state.controls.mouseButtons.wheel = CONSTANTS.controlsTransition.wheelMouse; + state.controls.mouseButtons.middle = CONSTANTS.controlsTransition.middleMouse; + + if (camMode === "ThirdPerson") { + setCamMode("FirstPerson"); + await switchToFirstPerson(state.controls, state.camera); + } else if (camMode === "FirstPerson") { + setCamMode("ThirdPerson"); + await switchToThirdPerson(state.controls, state.camera); + } + + setIsTransitioning && setIsTransitioning(false); +} diff --git a/app/src/modules/scene/camera/switchToFirstPerson.ts b/app/src/modules/scene/camera/functions/switchToFirstPerson.ts similarity index 91% rename from app/src/modules/scene/camera/switchToFirstPerson.ts rename to app/src/modules/scene/camera/functions/switchToFirstPerson.ts index a5371c4..a8a35de 100644 --- a/app/src/modules/scene/camera/switchToFirstPerson.ts +++ b/app/src/modules/scene/camera/functions/switchToFirstPerson.ts @@ -1,25 +1,25 @@ -import * as THREE from 'three'; -import * as CONSTANTS from '../../../types/world/worldConstants'; - -export default async function switchToFirstPerson( - controls: any, - camera: any -) { - if (!controls) return; - - const cameraDirection = new THREE.Vector3(); - camera.getWorldDirection(cameraDirection); - cameraDirection.normalize(); - - await controls.setPosition(camera.position.x, 2, camera.position.z, true); - controls.setTarget(camera.position.x, 2, camera.position.z, true); - controls.mouseButtons.left = CONSTANTS.firstPersonControls.leftMouse; - controls.lockPointer(); - - controls.azimuthRotateSpeed = CONSTANTS.firstPersonControls.azimuthRotateSpeed; - controls.polarRotateSpeed = CONSTANTS.firstPersonControls.polarRotateSpeed; - controls.truckSpeed = CONSTANTS.firstPersonControls.truckSpeed; - controls.minDistance = CONSTANTS.firstPersonControls.minDistance; - controls.maxDistance = CONSTANTS.firstPersonControls.maxDistance; - controls.maxPolarAngle = CONSTANTS.firstPersonControls.maxPolarAngle; +import * as THREE from 'three'; +import * as CONSTANTS from '../../../../types/world/worldConstants'; + +export default async function switchToFirstPerson( + controls: any, + camera: any +) { + if (!controls) return; + + const cameraDirection = new THREE.Vector3(); + camera.getWorldDirection(cameraDirection); + cameraDirection.normalize(); + + await controls.setPosition(camera.position.x, 2, camera.position.z, true); + controls.setTarget(camera.position.x, 2, camera.position.z, true); + controls.mouseButtons.left = CONSTANTS.firstPersonControls.leftMouse; + controls.lockPointer(); + + controls.azimuthRotateSpeed = CONSTANTS.firstPersonControls.azimuthRotateSpeed; + controls.polarRotateSpeed = CONSTANTS.firstPersonControls.polarRotateSpeed; + controls.truckSpeed = CONSTANTS.firstPersonControls.truckSpeed; + controls.minDistance = CONSTANTS.firstPersonControls.minDistance; + controls.maxDistance = CONSTANTS.firstPersonControls.maxDistance; + controls.maxPolarAngle = CONSTANTS.firstPersonControls.maxPolarAngle; } \ No newline at end of file diff --git a/app/src/modules/scene/camera/switchToThirdPerson.ts b/app/src/modules/scene/camera/functions/switchToThirdPerson.ts similarity index 93% rename from app/src/modules/scene/camera/switchToThirdPerson.ts rename to app/src/modules/scene/camera/functions/switchToThirdPerson.ts index 1e59749..b66adc5 100644 --- a/app/src/modules/scene/camera/switchToThirdPerson.ts +++ b/app/src/modules/scene/camera/functions/switchToThirdPerson.ts @@ -1,29 +1,29 @@ -import * as THREE from 'three'; -import * as CONSTANTS from '../../../types/world/worldConstants'; - -export default async function switchToThirdPerson( - controls: any, - camera: any -) { - if (!controls) return; - controls.mouseButtons.left = CONSTANTS.thirdPersonControls.leftMouse; - controls.mouseButtons.right = CONSTANTS.thirdPersonControls.rightMouse; - controls.mouseButtons.middle = CONSTANTS.thirdPersonControls.middleMouse; - controls.mouseButtons.wheel = CONSTANTS.thirdPersonControls.wheelMouse; - controls.unlockPointer(); - - const cameraDirection = new THREE.Vector3(); - camera.getWorldDirection(cameraDirection); - const targetOffset = cameraDirection.multiplyScalar(CONSTANTS.thirdPersonControls.targetOffset); - const targetPosition = new THREE.Vector3(camera.position.x, camera.position.y, camera.position.z).add(targetOffset); - - controls.setPosition(camera.position.x, CONSTANTS.thirdPersonControls.cameraHeight, camera.position.z, true); - controls.setTarget(targetPosition.x, 0, targetPosition.z, true); - - controls.azimuthRotateSpeed = CONSTANTS.thirdPersonControls.azimuthRotateSpeed; - controls.polarRotateSpeed = CONSTANTS.thirdPersonControls.polarRotateSpeed; - controls.truckSpeed = CONSTANTS.thirdPersonControls.truckSpeed; - controls.minDistance = CONSTANTS.threeDimension.minDistance; - controls.maxDistance = CONSTANTS.thirdPersonControls.maxDistance; - controls.maxPolarAngle = CONSTANTS.thirdPersonControls.maxPolarAngle; +import * as THREE from 'three'; +import * as CONSTANTS from '../../../../types/world/worldConstants'; + +export default async function switchToThirdPerson( + controls: any, + camera: any +) { + if (!controls) return; + controls.mouseButtons.left = CONSTANTS.thirdPersonControls.leftMouse; + controls.mouseButtons.right = CONSTANTS.thirdPersonControls.rightMouse; + controls.mouseButtons.middle = CONSTANTS.thirdPersonControls.middleMouse; + controls.mouseButtons.wheel = CONSTANTS.thirdPersonControls.wheelMouse; + controls.unlockPointer(); + + const cameraDirection = new THREE.Vector3(); + camera.getWorldDirection(cameraDirection); + const targetOffset = cameraDirection.multiplyScalar(CONSTANTS.thirdPersonControls.targetOffset); + const targetPosition = new THREE.Vector3(camera.position.x, camera.position.y, camera.position.z).add(targetOffset); + + controls.setPosition(camera.position.x, CONSTANTS.thirdPersonControls.cameraHeight, camera.position.z, true); + controls.setTarget(targetPosition.x, 0, targetPosition.z, true); + + controls.azimuthRotateSpeed = CONSTANTS.thirdPersonControls.azimuthRotateSpeed; + controls.polarRotateSpeed = CONSTANTS.thirdPersonControls.polarRotateSpeed; + controls.truckSpeed = CONSTANTS.thirdPersonControls.truckSpeed; + controls.minDistance = CONSTANTS.threeDimension.minDistance; + controls.maxDistance = CONSTANTS.thirdPersonControls.maxDistance; + controls.maxPolarAngle = CONSTANTS.thirdPersonControls.maxPolarAngle; } \ No newline at end of file diff --git a/app/src/modules/scene/camera/updateCameraPosition.ts b/app/src/modules/scene/camera/functions/updateCameraPosition.ts similarity index 88% rename from app/src/modules/scene/camera/updateCameraPosition.ts rename to app/src/modules/scene/camera/functions/updateCameraPosition.ts index 26e22ed..fb8c956 100644 --- a/app/src/modules/scene/camera/updateCameraPosition.ts +++ b/app/src/modules/scene/camera/functions/updateCameraPosition.ts @@ -1,26 +1,26 @@ -import { Socket } from "socket.io-client"; -import * as THREE from "three"; -import { getUserData } from "../../../functions/getUserData"; - -export default function updateCamPosition( - controls: any, - socket: Socket, - position: THREE.Vector3, - rotation: THREE.Euler, - projectId?: string -) { - const { userId, organization } = getUserData(); - if (!controls.current) return; - const target = controls.current.getTarget(new THREE.Vector3()); - - const camData = { - organization, - userId: userId, - position: position, - target: new THREE.Vector3(target.x, 0, target.z), - rotation: new THREE.Vector3(rotation.x, rotation.y, rotation.z), - socketId: socket.id, - projectId, - }; - socket.emit("v1:Camera:set", camData); -} +import { Socket } from "socket.io-client"; +import * as THREE from "three"; +import { getUserData } from "../../../../functions/getUserData"; + +export default function updateCamPosition( + controls: any, + socket: Socket, + position: THREE.Vector3, + rotation: THREE.Euler, + projectId?: string +) { + const { userId, organization } = getUserData(); + if (!controls.current) return; + const target = controls.current.getTarget(new THREE.Vector3()); + + const camData = { + organization, + userId: userId, + position: position, + target: new THREE.Vector3(target.x, 0, target.z), + rotation: new THREE.Vector3(rotation.x, rotation.y, rotation.z), + socketId: socket.id, + projectId, + }; + socket.emit("v1:Camera:set", camData); +} diff --git a/app/src/hooks/useCameraShortcuts.ts b/app/src/modules/scene/camera/shortcutsControls/cameraShortcutsControls.tsx similarity index 50% rename from app/src/hooks/useCameraShortcuts.ts rename to app/src/modules/scene/camera/shortcutsControls/cameraShortcutsControls.tsx index e4b897a..a073e40 100644 --- a/app/src/hooks/useCameraShortcuts.ts +++ b/app/src/modules/scene/camera/shortcutsControls/cameraShortcutsControls.tsx @@ -3,20 +3,24 @@ import { useThree } from "@react-three/fiber"; import * as THREE from "three"; import type { CameraControls } from "@react-three/drei"; -export const useCameraShortcuts = (controlsRef: React.RefObject) => { - const { camera } = useThree(); +const CameraShortcutsControls = () => { + const { camera, controls } = useThree(); useEffect(() => { const handleKeyDown = (e: KeyboardEvent) => { - if (!controlsRef.current) return; + if (!controls) return; - // get current distance from camera to target + const cc = controls as CameraControls; + + // get current target const target = new THREE.Vector3(); - controlsRef.current.getTarget(target); + cc.getTarget(target); const distance = camera.position.distanceTo(target); let pos: THREE.Vector3 | null = null; + const dir = new THREE.Vector3().subVectors(camera.position, target).normalize(); + switch (e.key) { case "1": // Front pos = new THREE.Vector3(0, 0, distance).add(target); @@ -27,13 +31,24 @@ export const useCameraShortcuts = (controlsRef: React.RefObject) case "7": // Top pos = new THREE.Vector3(0, distance, 0).add(target); break; - case "9": // Back - pos = new THREE.Vector3(0, 0, -distance).add(target); + case "9": { + // Opposite view logic + if (Math.abs(dir.z) > Math.abs(dir.x) && Math.abs(dir.z) > Math.abs(dir.y)) { + // Currently looking Front/Back → flip Z + pos = new THREE.Vector3(0, 0, -Math.sign(dir.z) * distance).add(target); + } else if (Math.abs(dir.x) > Math.abs(dir.z) && Math.abs(dir.x) > Math.abs(dir.y)) { + // Currently looking Right/Left → flip X + pos = new THREE.Vector3(-Math.sign(dir.x) * distance, 0, 0).add(target); + } else { + // Currently looking Top/Bottom → stay Top + pos = new THREE.Vector3(0, distance, 0).add(target); + } break; + } } if (pos) { - controlsRef.current.setLookAt( + cc.setLookAt( pos.x, pos.y, pos.z, // camera position target.x, target.y, target.z, // keep same target true // smooth transition @@ -43,5 +58,9 @@ export const useCameraShortcuts = (controlsRef: React.RefObject) window.addEventListener("keydown", handleKeyDown); return () => window.removeEventListener("keydown", handleKeyDown); - }, [controlsRef, camera]); + }, [controls, camera]); + + return null; }; + +export default CameraShortcutsControls; diff --git a/app/src/modules/scene/controls/controls.tsx b/app/src/modules/scene/controls/controls.tsx index d63f0ac..a3c9638 100644 --- a/app/src/modules/scene/controls/controls.tsx +++ b/app/src/modules/scene/controls/controls.tsx @@ -3,22 +3,22 @@ import { useRef, useEffect } from "react"; import { useThree } from "@react-three/fiber"; import * as THREE from "three"; import * as CONSTANTS from '../../../types/world/worldConstants'; - import { useSocketStore, useToggleView, useResetCamera } from "../../../store/builder/store"; -import { getCamera } from "../../../services/factoryBuilder/camera/getCameraApi"; -import updateCamPosition from "../camera/updateCameraPosition"; + import CamMode from "../camera/camMode"; import SwitchView from "../camera/switchView"; -import SelectionControls3D from "./selectionControls/selection3D/selectionControls3D"; -import TransformControl from "./transformControls/transformControls"; -import { useParams } from "react-router-dom"; -import { getUserData } from "../../../functions/getUserData"; - import ContextControls from "./contextControls/contextControls"; +import TransformControl from "./transformControls/transformControls"; import SelectionControls2D from "./selectionControls/selection2D/selectionControls2D"; +import SelectionControls3D from "./selectionControls/selection3D/selectionControls3D"; import UndoRedo2DControls from "./undoRedoControls/undoRedo2D/undoRedo2DControls"; import UndoRedo3DControls from "./undoRedoControls/undoRedo3D/undoRedo3DControls"; -import { useCameraShortcuts } from "../../../hooks/useCameraShortcuts"; +import CameraShortcutsControls from "../camera/shortcutsControls/cameraShortcutsControls"; + +import { useParams } from "react-router-dom"; +import { getUserData } from "../../../functions/getUserData"; +import { getCamera } from "../../../services/factoryBuilder/camera/getCameraApi"; +import updateCamPosition from "../camera/functions/updateCameraPosition"; export default function Controls() { const controlsRef = useRef(null); @@ -117,7 +117,6 @@ export default function Controls() { stopInterval(); }; }, [toggleView, state, socket]); - useCameraShortcuts(controlsRef); return ( <> @@ -140,6 +139,8 @@ export default function Controls() { + + From 547fd1af120310f9ca7032d5c84f0a90fbc5c777 Mon Sep 17 00:00:00 2001 From: Jerald-Golden-B Date: Tue, 26 Aug 2025 14:43:38 +0530 Subject: [PATCH 09/34] bug fix in wall, wall Asset, floor , and decal selection and unselection, added decal deletion --- .../components/layout/sidebarLeft/Assets.tsx | 6 +- .../layout/sidebarRight/SideBarRight.tsx | 570 ++++++++---------- .../customInput/RotationInput.tsx | 11 +- .../properties/AssetProperties.tsx | 2 + .../properties/DecalProperties.tsx | 53 -- .../properties/SelectedDecalProperties.tsx | 49 ++ .../modules/builder/Decal/decalInstance.tsx | 117 +++- .../Instances/Instance/floorInstance.tsx | 4 +- .../floor/Instances/floorInstances.tsx | 17 +- app/src/modules/builder/floor/floorGroup.tsx | 6 +- .../builder/wall/Instances/instance/wall.tsx | 20 +- .../builder/wall/Instances/wallInstances.tsx | 19 +- app/src/modules/builder/wall/wallGroup.tsx | 6 +- .../builder/wallAsset/wallAssetCreator.tsx | 6 +- .../builder/wallAsset/wallAssetGroup.tsx | 14 +- app/src/modules/builder/zone/zoneGroup.tsx | 6 +- .../scene/postProcessing/postProcessing.tsx | 37 +- app/src/store/builder/store.ts | 1 + app/src/store/builder/useBuilderStore.ts | 9 + app/src/store/builder/useFloorStore.ts | 20 +- app/src/store/builder/useWallStore.ts | 20 +- 21 files changed, 558 insertions(+), 435 deletions(-) delete mode 100644 app/src/components/layout/sidebarRight/properties/DecalProperties.tsx create mode 100644 app/src/components/layout/sidebarRight/properties/SelectedDecalProperties.tsx diff --git a/app/src/components/layout/sidebarLeft/Assets.tsx b/app/src/components/layout/sidebarLeft/Assets.tsx index c08ef78..2d4aa34 100644 --- a/app/src/components/layout/sidebarLeft/Assets.tsx +++ b/app/src/components/layout/sidebarLeft/Assets.tsx @@ -127,6 +127,7 @@ const Assets: React.FC = () => { ]; const { selectedSubCategory, setSelectedSubCategory } = useDecalStore(); + return (
@@ -208,9 +209,8 @@ const Assets: React.FC = () => { {activeSubcategories.map((cat, index) => (
setSelectedSubCategory(cat.name)} >
{cat.icon}
diff --git a/app/src/components/layout/sidebarRight/SideBarRight.tsx b/app/src/components/layout/sidebarRight/SideBarRight.tsx index 0041aaf..ae9c908 100644 --- a/app/src/components/layout/sidebarRight/SideBarRight.tsx +++ b/app/src/components/layout/sidebarRight/SideBarRight.tsx @@ -1,29 +1,13 @@ import React, { useEffect, useState } from "react"; import Header from "./Header"; -import useModuleStore, { - useSubModuleStore, -} from "../../../store/useModuleStore"; -import { - AnalysisIcon, - FilePackageIcon, - MechanicsIcon, - PropertiesIcon, - SimulationIcon, -} from "../../icons/SimulationIcons"; +import useModuleStore, { useSubModuleStore } from "../../../store/useModuleStore"; +import { AnalysisIcon, FilePackageIcon, MechanicsIcon, PropertiesIcon, SimulationIcon, } from "../../icons/SimulationIcons"; import { useToggleStore } from "../../../store/useUIToggleStore"; import Visualization from "./visualization/Visualization"; import Analysis from "./analysis/Analysis"; import Simulations from "./simulation/Simulations"; -import useVersionHistoryVisibleStore, { - useDecalStore, - useSaveVersion, - useSelectedFloorItem, - useToolMode, -} from "../../../store/builder/store"; -import { - useSelectedEventData, - useSelectedEventSphere, -} from "../../../store/simulation/useSimulationStore"; +import useVersionHistoryVisibleStore, { useDecalStore, useSaveVersion, useSelectedFloorItem, useToolMode, } from "../../../store/builder/store"; +import { useSelectedEventData, useSelectedEventSphere, } from "../../../store/simulation/useSimulationStore"; import { useBuilderStore } from "../../../store/builder/useBuilderStore"; import GlobalProperties from "./properties/GlobalProperties"; import AssetProperties from "./properties/AssetProperties"; @@ -36,318 +20,272 @@ import FloorProperties from "./properties/FloorProperties"; import SelectedWallProperties from "./properties/SelectedWallProperties"; import SelectedFloorProperties from "./properties/SelectedFloorProperties"; import ResourceManagement from "./resourceManagement/ResourceManagement"; -import DecalProperties from "./properties/DecalProperties"; +import SelectedDecalProperties from "./properties/SelectedDecalProperties"; type DisplayComponent = - | "versionHistory" - | "globalProperties" - | "aisleProperties" - | "wallProperties" - | "floorProperties" - | "assetProperties" - | "selectedWallProperties" - | "selectedFloorProperties" - | "zoneProperties" - | "simulations" - | "mechanics" - | "analysis" - | "visualization" - | "selectedDecalProperties" - | "resourceManagement" - | "none"; + | "versionHistory" + | "globalProperties" + | "aisleProperties" + | "wallProperties" + | "floorProperties" + | "assetProperties" + | "selectedWallProperties" + | "selectedFloorProperties" + | "zoneProperties" + | "simulations" + | "mechanics" + | "analysis" + | "visualization" + | "selectedDecalProperties" + | "resourceManagement" + | "none"; const SideBarRight: React.FC = () => { - const { activeModule } = useModuleStore(); - const { toggleUIRight } = useToggleStore(); - const { toolMode } = useToolMode(); - const { subModule, setSubModule } = useSubModuleStore(); - const { selectedFloorItem } = useSelectedFloorItem(); - const { selectedWall, selectedFloor, selectedAisle } = useBuilderStore(); - const { selectedEventData } = useSelectedEventData(); - const { selectedEventSphere } = useSelectedEventSphere(); - const { viewVersionHistory, setVersionHistoryVisible } = - useVersionHistoryVisibleStore(); - const { isVersionSaved } = useSaveVersion(); - const { selectedSubCategory } = useDecalStore(); + const { selectedDecal } = useBuilderStore(); + const { activeModule } = useModuleStore(); + const { toggleUIRight } = useToggleStore(); + const { toolMode } = useToolMode(); + const { subModule, setSubModule } = useSubModuleStore(); + const { selectedFloorItem } = useSelectedFloorItem(); + const { selectedWall, selectedFloor, selectedAisle } = useBuilderStore(); + const { selectedEventData } = useSelectedEventData(); + const { selectedEventSphere } = useSelectedEventSphere(); + const { viewVersionHistory, setVersionHistoryVisible } = useVersionHistoryVisibleStore(); + const { isVersionSaved } = useSaveVersion(); - const [displayComponent, setDisplayComponent] = - useState("none"); + const [displayComponent, setDisplayComponent] = useState("none"); - useEffect(() => { - if (activeModule !== "simulation") setSubModule("properties"); - if (activeModule === "simulation") setSubModule("simulations"); - }, [activeModule, setSubModule]); + useEffect(() => { + if (activeModule !== "simulation") setSubModule("properties"); + if (activeModule === "simulation") setSubModule("simulations"); + }, [activeModule, setSubModule]); - useEffect(() => { - if ( - activeModule !== "mechanics" && - selectedEventData && - selectedEventSphere - ) { - setSubModule("mechanics"); - } else if (!selectedEventData && !selectedEventSphere) { - if (activeModule === "simulation") { - setSubModule("simulations"); - } - } - if (activeModule !== "simulation") { - setSubModule("properties"); - } - }, [activeModule, selectedEventData, selectedEventSphere, setSubModule]); - - useEffect(() => { - if (activeModule === "visualization") { - setDisplayComponent("visualization"); - return; - } - - if (!isVersionSaved && activeModule === "simulation") { - if (subModule === "simulations") { - setDisplayComponent("simulations"); - return; - } - if (subModule === "mechanics") { - setDisplayComponent("mechanics"); - return; - } - if (subModule === "analysis") { - setDisplayComponent("analysis"); - return; - } - if (subModule === "resourceManagement") { - setDisplayComponent("resourceManagement"); - return; - } - } - - if (activeModule === "simulation" || activeModule === "builder") { - if (subModule === "resourceManagement") { - setDisplayComponent("resourceManagement"); - return; - } - } - - if (subModule === "properties" && activeModule !== "visualization") { - if (selectedFloorItem) { - setDisplayComponent("assetProperties"); - return; - } - if ( - !selectedFloorItem && - !selectedFloor && - !selectedAisle && - selectedWall - ) { - setDisplayComponent("selectedWallProperties"); - return; - } - if ( - !selectedFloorItem && - !selectedWall && - !selectedAisle && - selectedFloor - ) { - setDisplayComponent("selectedFloorProperties"); - return; - } - if (viewVersionHistory) { - setDisplayComponent("versionHistory"); - return; - } - if (selectedSubCategory) { - setDisplayComponent("selectedDecalProperties"); - return; - } - if ( - !selectedFloorItem && - !selectedFloor && - !selectedWall && - !selectedSubCategory - ) { - if (toolMode === "Aisle") { - setDisplayComponent("aisleProperties"); - return; + useEffect(() => { + if (activeModule !== "mechanics" && selectedEventData && selectedEventSphere) { + setSubModule("mechanics"); + } else if (!selectedEventData && !selectedEventSphere) { + if (activeModule === "simulation") { + setSubModule("simulations"); + } } - if (toolMode === "Wall") { - setDisplayComponent("wallProperties"); - return; + if (activeModule !== "simulation") { + setSubModule("properties"); } - if (toolMode === "Floor") { - setDisplayComponent("floorProperties"); - return; + }, [activeModule, selectedEventData, selectedEventSphere, setSubModule]); + + useEffect(() => { + if (activeModule === "visualization") { + setDisplayComponent("visualization"); + return; } - setDisplayComponent("globalProperties"); - return; - } - } - if ( - subModule === "zoneProperties" && - (activeModule === "builder" || activeModule === "simulation") - ) { - setDisplayComponent("zoneProperties"); - return; - } + if (!isVersionSaved && activeModule === "simulation") { + if (subModule === "simulations") { + setDisplayComponent("simulations"); + return; + } + if (subModule === "mechanics") { + setDisplayComponent("mechanics"); + return; + } + if (subModule === "analysis") { + setDisplayComponent("analysis"); + return; + } + if (subModule === "resourceManagement") { + setDisplayComponent("resourceManagement"); + return; + } + } - setDisplayComponent("none"); - }, [ - viewVersionHistory, - activeModule, - subModule, - isVersionSaved, - selectedFloorItem, - selectedWall, - selectedFloor, - selectedAisle, - toolMode, - selectedSubCategory, - ]); + if (activeModule === "simulation" || activeModule === "builder") { + if (subModule === "resourceManagement") { + setDisplayComponent("resourceManagement"); + return; + } + } - const renderComponent = () => { - switch (displayComponent) { - case "versionHistory": - return ; - case "globalProperties": - return ; - case "aisleProperties": - return ; - case "wallProperties": - return ; - case "floorProperties": - return ; - case "assetProperties": - return ; - case "selectedWallProperties": - return ; - case "selectedFloorProperties": - return ; - case "zoneProperties": - return ; - case "simulations": - return ; - case "mechanics": - return ; - case "analysis": - return ; - case "visualization": - return ; - case "selectedDecalProperties": - return ; - case "resourceManagement": - return ; - default: - return null; - } - }; + if (subModule === "properties" && activeModule !== "visualization") { + if (selectedFloorItem) { + setDisplayComponent("assetProperties"); + return; + } + if (!selectedFloorItem && !selectedFloor && !selectedAisle && selectedWall) { + setDisplayComponent("selectedWallProperties"); + return; + } + if (!selectedFloorItem && !selectedWall && !selectedAisle && selectedFloor) { + setDisplayComponent("selectedFloorProperties"); + return; + } + if (viewVersionHistory) { + setDisplayComponent("versionHistory"); + return; + } + if (selectedDecal) { + setDisplayComponent("selectedDecalProperties"); + return; + } + if (!selectedFloorItem && !selectedFloor && !selectedWall && !selectedDecal) { + if (toolMode === "Aisle") { + setDisplayComponent("aisleProperties"); + return; + } + if (toolMode === "Wall") { + setDisplayComponent("wallProperties"); + return; + } + if (toolMode === "Floor") { + setDisplayComponent("floorProperties"); + return; + } + setDisplayComponent("globalProperties"); + return; + } + } - return ( -
-
- {toggleUIRight && ( - <> - {(!isVersionSaved || activeModule !== "simulation") && ( -
- {activeModule !== "simulation" && ( + if (subModule === "zoneProperties" && (activeModule === "builder" || activeModule === "simulation")) { + setDisplayComponent("zoneProperties"); + return; + } + + setDisplayComponent("none"); + }, [viewVersionHistory, activeModule, subModule, isVersionSaved, selectedFloorItem, selectedWall, selectedFloor, selectedAisle, toolMode, selectedDecal]); + + const renderComponent = () => { + switch (displayComponent) { + case "versionHistory": + return ; + case "globalProperties": + return ; + case "aisleProperties": + return ; + case "wallProperties": + return ; + case "floorProperties": + return ; + case "assetProperties": + return ; + case "selectedWallProperties": + return ; + case "selectedFloorProperties": + return ; + case "zoneProperties": + return ; + case "simulations": + return ; + case "mechanics": + return ; + case "analysis": + return ; + case "visualization": + return ; + case "selectedDecalProperties": + return ; + case "resourceManagement": + return ; + default: + return null; + } + }; + + return ( +
+
+ {toggleUIRight && ( <> - + {(!isVersionSaved || activeModule !== "simulation") && ( +
+ {activeModule !== "simulation" && ( + <> + + + )} + + {activeModule === "simulation" && ( + <> + + + + + )} + + {(activeModule === "builder" || + activeModule === "simulation") && ( + + )} +
+ )} + + {displayComponent !== "none" && ( +
+
+ {renderComponent()} + {/* */} +
+
+ )} - )} - - {activeModule === "simulation" && ( - <> - - - - - )} - - {(activeModule === "builder" || - activeModule === "simulation") && ( - - )} -
- )} - - {displayComponent !== "none" && ( -
-
- {renderComponent()} - {/* */} -
-
- )} - - )} -
- ); + )} +
+ ); }; export default SideBarRight; diff --git a/app/src/components/layout/sidebarRight/customInput/RotationInput.tsx b/app/src/components/layout/sidebarRight/customInput/RotationInput.tsx index 962e967..ccddafa 100644 --- a/app/src/components/layout/sidebarRight/customInput/RotationInput.tsx +++ b/app/src/components/layout/sidebarRight/customInput/RotationInput.tsx @@ -1,30 +1,37 @@ import React from "react"; interface RotationInputProps { + heading?: string; // Optional label for the input + label?: string; // Optional label for the input onChange: (value: string) => void; // Callback for value change placeholder?: string; // Optional placeholder type?: string; // Input type (e.g., text, number, email) value?: number; + disabled?: boolean; // Disable the input if true } const RotationInput: React.FC = ({ + label = "Rotate :", // Default label + heading = "Rotation", // Default heading onChange, placeholder = "Enter value", // Default placeholder type = "number", // Default type value = "number", + disabled = false, }) => { return (
-
Rotation
+
{heading}
-
Rotate :
+
{label}
onChange(e.target.value)} placeholder={placeholder} value={value} + disabled={disabled} />
diff --git a/app/src/components/layout/sidebarRight/properties/AssetProperties.tsx b/app/src/components/layout/sidebarRight/properties/AssetProperties.tsx index 9c8b979..3a36e6c 100644 --- a/app/src/components/layout/sidebarRight/properties/AssetProperties.tsx +++ b/app/src/components/layout/sidebarRight/properties/AssetProperties.tsx @@ -57,6 +57,7 @@ const AssetProperties: React.FC = () => {
{objectPosition && ( { }} value1={parseFloat(objectPosition.x.toFixed(5))} value2={parseFloat(objectPosition.z.toFixed(5))} @@ -64,6 +65,7 @@ const AssetProperties: React.FC = () => { )} {objectRotation && ( { }} value={parseFloat(objectRotation.y.toFixed(5))} /> diff --git a/app/src/components/layout/sidebarRight/properties/DecalProperties.tsx b/app/src/components/layout/sidebarRight/properties/DecalProperties.tsx deleted file mode 100644 index 4163ea5..0000000 --- a/app/src/components/layout/sidebarRight/properties/DecalProperties.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import { - LayeringBottomIcon, - LayeringTopIcon, -} from "../../../icons/ExportCommonIcons"; -import InputRange from "../../../ui/inputs/InputRange"; -import RotationInput from "../customInput/RotationInput"; -import Vector3Input from "../customInput/Vector3Input"; - -const DecalProperties = () => { - return ( -
-
Decal Propertis
-
- {}} - value={10} - /> - console.log(value)} - header="Scale" - value={[0, 0, 0] as [number, number, number]} - /> -
- -
- console.log(value)} - key={"6"} - /> - -
-
Layering
- -
- - -
-
-
-
- ); -}; - -export default DecalProperties; diff --git a/app/src/components/layout/sidebarRight/properties/SelectedDecalProperties.tsx b/app/src/components/layout/sidebarRight/properties/SelectedDecalProperties.tsx new file mode 100644 index 0000000..1db536b --- /dev/null +++ b/app/src/components/layout/sidebarRight/properties/SelectedDecalProperties.tsx @@ -0,0 +1,49 @@ +import { LayeringBottomIcon, LayeringTopIcon } from "../../../icons/ExportCommonIcons"; +import InputRange from "../../../ui/inputs/InputRange"; +import RotationInput from "../customInput/RotationInput"; + +const SelectedDecalProperties = () => { + return ( +
+
Decal Properties
+
+ { }} + value={10} + /> + { }} + value={10} + /> +
+ +
+ console.log(value)} + /> + +
+
Layering
+ +
+ + +
+
+
+
+ ); +}; + +export default SelectedDecalProperties; diff --git a/app/src/modules/builder/Decal/decalInstance.tsx b/app/src/modules/builder/Decal/decalInstance.tsx index 1b68f3d..69b0c7c 100644 --- a/app/src/modules/builder/Decal/decalInstance.tsx +++ b/app/src/modules/builder/Decal/decalInstance.tsx @@ -1,33 +1,132 @@ import * as THREE from 'three'; import { Decal } from '@react-three/drei' import { useLoader } from '@react-three/fiber'; -import { useToggleView } from '../../../store/builder/store'; +import { useSocketStore, useToggleView, useToolMode } from '../../../store/builder/store'; import { useBuilderStore } from '../../../store/builder/useBuilderStore'; import defaultMaterial from '../../../assets/textures/floor/wall-tex.png'; import useModuleStore from '../../../store/useModuleStore'; +import { useSceneContext } from '../../scene/sceneContext'; +import { useEffect } from 'react'; +import { getUserData } from '../../../functions/getUserData'; +import { useVersionContext } from '../version/versionContext'; +import { useParams } from 'react-router-dom'; -function DecalInstance({ visible = true, decal, zPosition = decal.decalPosition[2] }: { visible?: boolean, decal: Decal, zPosition?: number }) { - const { setSelectedWall, setSelectedFloor, selectedDecal, setSelectedDecal } = useBuilderStore(); - const { togglView } = useToggleView(); +// import { upsertWallApi } from '../../../services/factoryBuilder/wall/upsertWallApi'; +// import { upsertFloorApi } from '../../../services/factoryBuilder/floor/upsertFloorApi'; + +function DecalInstance({ parent, visible = true, decal, zPosition = decal.decalPosition[2] }: { parent: Wall | Floor; visible?: boolean, decal: Decal, zPosition?: number }) { + const { setSelectedWall, setSelectedFloor, selectedDecal, deletableDecal, setSelectedDecal, setDeletableDecal } = useBuilderStore(); + const { wallStore, floorStore } = useSceneContext(); + const { removeDecal: removeDecalFromWall } = wallStore(); + const { removeDecal: removeDecalFromFloor } = floorStore(); + const { toolMode } = useToolMode(); + const { toggleView } = useToggleView(); const { activeModule } = useModuleStore(); + const { userId, organization } = getUserData(); + const { selectedVersionStore } = useVersionContext(); + const { selectedVersion } = selectedVersionStore(); + const { projectId } = useParams(); + const { socket } = useSocketStore(); const material = useLoader(THREE.TextureLoader, defaultMaterial); + useEffect(() => { + if (!toggleView && activeModule === 'builder') { + if (toolMode !== 'cursor') { + if (selectedDecal) setSelectedDecal(null); + } + if (toolMode !== '3D-Delete') { + if (deletableDecal) setDeletableDecal(null); + } + } else { + if (selectedDecal) setSelectedDecal(null); + if (deletableDecal) setDeletableDecal(null); + } + }, [toggleView, toolMode, activeModule, selectedDecal, deletableDecal]); + + const deleteDecal = (decalUuid: string, parent: Wall | Floor) => { + if ('wallUuid' in parent) { + const updatedWall = removeDecalFromWall(decalUuid); + + if (projectId && updatedWall) { + // API + + // upsertWallApi(projectId, selectedVersion?.versionId || '', updatedWall); + + // SOCKET + + const data = { + wallData: updatedWall, + projectId: projectId, + versionId: selectedVersion?.versionId || '', + userId: userId, + organization: organization + } + + socket.emit('v1:model-Wall:add', data); + } + } else if ('floorUuid' in parent) { + const updatedFloor = removeDecalFromFloor(decalUuid); + + if (projectId && updatedFloor) { + // API + + // upsertFloorApi(projectId, selectedVersion?.versionId || '', updatedFloor); + + // SOCKET + + const data = { + floorData: updatedFloor, + projectId: projectId, + versionId: selectedVersion?.versionId || '', + userId: userId, + organization: organization + } + + socket.emit('v1:model-Floor:add', data); + } + } + } + return ( { - if (visible && !togglView && activeModule === 'builder') { + if (visible && !toggleView && activeModule === 'builder') { if (e.object.userData.decalUuid) { e.stopPropagation(); - setSelectedDecal(e.object); - setSelectedWall(null); - setSelectedFloor(null); + if (toolMode === 'cursor') { + setSelectedDecal(e.object); + setSelectedWall(null); + setSelectedFloor(null); + } else if (toolMode === '3D-Delete') { + deleteDecal(e.object.userData.decalUuid, parent); + } + } + } + }} + onPointerEnter={(e) => { + if (visible && !toggleView && activeModule === 'builder') { + if (e.object.userData.decalUuid) { + e.stopPropagation(); + if (toolMode === '3D-Delete') { + setDeletableDecal(e.object); + } + } + } + }} + onPointerLeave={(e) => { + if (visible && !toggleView && activeModule === 'builder') { + if (e.object.userData.decalUuid) { + e.stopPropagation(); + if (toolMode === '3D-Delete' && deletableDecal && deletableDecal?.userData.decalUuid === e.object.userData.decalUuid) { + setDeletableDecal(null); + } } } }} diff --git a/app/src/modules/builder/floor/Instances/Instance/floorInstance.tsx b/app/src/modules/builder/floor/Instances/Instance/floorInstance.tsx index aa60691..f89b38d 100644 --- a/app/src/modules/builder/floor/Instances/Instance/floorInstance.tsx +++ b/app/src/modules/builder/floor/Instances/Instance/floorInstance.tsx @@ -28,7 +28,7 @@ import material4MetalicMap from "../../../../../assets/textures/floor/tex3/metal import material4NormalMap from "../../../../../assets/textures/floor/tex3/metal_plate_nor_gl_1k.png"; function FloorInstance({ floor }: { floor: Floor }) { - const { togglView } = useToggleView(); + const { toggleView } = useToggleView(); const { activeModule } = useModuleStore(); const { selectedFloor, setSelectedFloor, setSelectedDecal } = useBuilderStore(); const savedTheme = localStorage.getItem("theme"); @@ -159,7 +159,7 @@ function FloorInstance({ floor }: { floor: Floor }) { position={[0, !floor.isBeveled ? floor.floorDepth - 0.1 : floor.floorDepth - 0.2, 0,]} userData={floor} onDoubleClick={(e) => { - if (!togglView && activeModule === "builder") { + if (!toggleView && activeModule === "builder") { if (e.object.userData.floorUuid) { e.stopPropagation(); setSelectedFloor(e.object); diff --git a/app/src/modules/builder/floor/Instances/floorInstances.tsx b/app/src/modules/builder/floor/Instances/floorInstances.tsx index a578800..2af5787 100644 --- a/app/src/modules/builder/floor/Instances/floorInstances.tsx +++ b/app/src/modules/builder/floor/Instances/floorInstances.tsx @@ -2,21 +2,36 @@ import React, { useEffect, useMemo } from 'react'; import { Vector3 } from 'three'; import { Html } from '@react-three/drei'; import { useSceneContext } from '../../../scene/sceneContext'; -import { useToggleView } from '../../../../store/builder/store'; +import { useToggleView, useToolMode } from '../../../../store/builder/store'; import Line from '../../line/line'; import Point from '../../point/point'; import FloorInstance from './Instance/floorInstance'; import Floor2DInstance from './Instance/floor2DInstance'; +import useModuleStore from '../../../../store/useModuleStore'; +import { useBuilderStore } from '../../../../store/builder/useBuilderStore'; function FloorInstances() { const { floorStore } = useSceneContext(); const { floors } = floorStore(); + const { setSelectedFloor, selectedFloor } = useBuilderStore(); + const { toolMode } = useToolMode(); const { toggleView } = useToggleView(); + const { activeModule } = useModuleStore(); useEffect(() => { // console.log('floors: ', floors); }, [floors]) + useEffect(() => { + if (!toggleView && activeModule === 'builder') { + if (toolMode !== 'cursor') { + if (selectedFloor) setSelectedFloor(null); + } + } else { + if (selectedFloor) setSelectedFloor(null); + } + }, [toggleView, toolMode, activeModule, selectedFloor]); + const allPoints = useMemo(() => { const points: Point[] = []; const seenUuids = new Set(); diff --git a/app/src/modules/builder/floor/floorGroup.tsx b/app/src/modules/builder/floor/floorGroup.tsx index 47ea3b5..d782e1a 100644 --- a/app/src/modules/builder/floor/floorGroup.tsx +++ b/app/src/modules/builder/floor/floorGroup.tsx @@ -10,7 +10,7 @@ import FloorInstances from './Instances/floorInstances'; import { getFloorsApi } from '../../../services/factoryBuilder/floor/getFloorsApi'; function FloorGroup() { - const { togglView } = useToggleView(); + const { toggleView } = useToggleView(); const { setSelectedFloor, setSelectedDecal } = useBuilderStore(); const { activeModule } = useModuleStore(); const { activeTool } = useActiveTool(); @@ -21,11 +21,11 @@ function FloorGroup() { const { projectId } = useParams(); useEffect(() => { - if (togglView || activeModule !== 'builder') { + if (toggleView || activeModule !== 'builder') { setSelectedFloor(null); setSelectedDecal(null); } - }, [togglView, activeModule, activeTool]) + }, [toggleView, activeModule, activeTool]) useEffect(() => { if (projectId && selectedVersion) { diff --git a/app/src/modules/builder/wall/Instances/instance/wall.tsx b/app/src/modules/builder/wall/Instances/instance/wall.tsx index 8798b01..939dc78 100644 --- a/app/src/modules/builder/wall/Instances/instance/wall.tsx +++ b/app/src/modules/builder/wall/Instances/instance/wall.tsx @@ -17,11 +17,11 @@ import material1 from '../../../../../assets/textures/floor/factory wall texture function Wall({ wall }: { readonly wall: Wall }) { const { wallStore, wallAssetStore } = useSceneContext(); - const { walls } = wallStore(); + const { walls, addDecal } = wallStore(); const { wallAssets, getWallAssetsByWall, setVisibility } = wallAssetStore(); const assets = getWallAssetsByWall(wall.wallUuid); const { selectedWall, setSelectedWall, setSelectedDecal } = useBuilderStore(); - const { togglView } = useToggleView(); + const { toggleView } = useToggleView(); const { activeModule } = useModuleStore(); const { camera } = useThree(); const { wallVisibility } = useWallVisibility(); @@ -154,11 +154,23 @@ function Wall({ wall }: { readonly wall: Wall }) { userData={wall} name={`WallReference_${wall.wallUuid}`} onDoubleClick={(e) => { - if (visible && !togglView && activeModule === 'builder') { + if (visible && !toggleView && activeModule === 'builder') { if (e.object.userData.wallUuid) { e.stopPropagation(); setSelectedWall(e.object); setSelectedDecal(null); + + if (wall.decals.length > 0) return; + const decal: Decal = { + decalUuid: THREE.MathUtils.generateUUID(), + decalName: 'Decal', + decalId: 'Default Decal', + decalPosition: [0, 0, wall.wallThickness / 2 + 0.001], + decalRotation: 0, + decalScale: 1, + decalType: { type: 'Wall', wallUuid: wall.wallUuid } + } + addDecal(wall.wallUuid, decal); } } }} @@ -171,7 +183,7 @@ function Wall({ wall }: { readonly wall: Wall }) { {wall.decals.map((decal) => ( - + ))} diff --git a/app/src/modules/builder/wall/Instances/wallInstances.tsx b/app/src/modules/builder/wall/Instances/wallInstances.tsx index 254705a..a67e3a3 100644 --- a/app/src/modules/builder/wall/Instances/wallInstances.tsx +++ b/app/src/modules/builder/wall/Instances/wallInstances.tsx @@ -2,8 +2,9 @@ import React, { useEffect, useMemo } from 'react'; import { DoubleSide, RepeatWrapping, Shape, SRGBColorSpace, TextureLoader, Vector2, Vector3 } from 'three'; import { Html, Extrude } from '@react-three/drei'; import { useLoader } from '@react-three/fiber'; +import { useBuilderStore } from '../../../../store/builder/useBuilderStore'; import { useSceneContext } from '../../../scene/sceneContext'; -import { useToggleView } from '../../../../store/builder/store'; +import { useToggleView, useToolMode } from '../../../../store/builder/store'; import { useWallClassification } from './instance/helpers/useWallClassification'; import Line from '../../line/line'; import Point from '../../point/point'; @@ -12,17 +13,31 @@ import * as Constants from '../../../../types/world/worldConstants'; import texturePath from "../../../../assets/textures/floor/white.png"; import texturePathDark from "../../../../assets/textures/floor/black.png"; +import useModuleStore from '../../../../store/useModuleStore'; function WallInstances() { const { wallStore } = useSceneContext(); + const { setSelectedWall, selectedWall } = useBuilderStore(); + const { toolMode } = useToolMode(); + const { toggleView } = useToggleView(); + const { activeModule } = useModuleStore(); const { walls } = wallStore(); const { rooms } = useWallClassification(walls); - const { toggleView } = useToggleView(); useEffect(() => { // console.log('walls: ', walls); }, [walls]) + useEffect(() => { + if (!toggleView && activeModule === 'builder') { + if (toolMode !== 'cursor') { + if (selectedWall) setSelectedWall(null); + } + } else { + if (selectedWall) setSelectedWall(null); + } + }, [toggleView, toolMode, activeModule, selectedWall]); + const allPoints = useMemo(() => { const points: Point[] = []; const seenUuids = new Set(); diff --git a/app/src/modules/builder/wall/wallGroup.tsx b/app/src/modules/builder/wall/wallGroup.tsx index 084b969..3561751 100644 --- a/app/src/modules/builder/wall/wallGroup.tsx +++ b/app/src/modules/builder/wall/wallGroup.tsx @@ -11,7 +11,7 @@ import WallInstances from './Instances/wallInstances'; import { getWallsApi } from '../../../services/factoryBuilder/wall/getWallsApi'; function WallGroup() { - const { togglView } = useToggleView(); + const { toggleView } = useToggleView(); const { setSelectedWall, setSelectedDecal } = useBuilderStore(); const { activeModule } = useModuleStore(); const { activeTool } = useActiveTool(); @@ -22,11 +22,11 @@ function WallGroup() { const { projectId } = useParams(); useEffect(() => { - if (togglView || activeModule !== 'builder') { + if (toggleView || activeModule !== 'builder') { setSelectedWall(null); setSelectedDecal(null); } - }, [togglView, activeModule, activeTool]) + }, [toggleView, activeModule, activeTool]) useEffect(() => { if (projectId && selectedVersion) { diff --git a/app/src/modules/builder/wallAsset/wallAssetCreator.tsx b/app/src/modules/builder/wallAsset/wallAssetCreator.tsx index fb95236..13438c2 100644 --- a/app/src/modules/builder/wallAsset/wallAssetCreator.tsx +++ b/app/src/modules/builder/wallAsset/wallAssetCreator.tsx @@ -14,7 +14,7 @@ import closestPointOnLineSegment from '../line/helpers/getClosestPointOnLineSegm function WallAssetCreator() { const { socket } = useSocketStore(); const { pointer, camera, raycaster, scene, gl } = useThree(); - const { togglView } = useToggleView(); + const { toggleView } = useToggleView(); const { activeModule } = useModuleStore(); const { wallAssetStore } = useSceneContext(); const { addWallAsset } = wallAssetStore(); @@ -84,7 +84,7 @@ function WallAssetCreator() { } }; - if (!togglView && activeModule === 'builder') { + if (!toggleView && activeModule === 'builder') { canvasElement.addEventListener('drop', onDrop); } @@ -92,7 +92,7 @@ function WallAssetCreator() { canvasElement.removeEventListener('drop', onDrop); }; - }, [gl, camera, togglView, activeModule, socket, selectedItem, setSelectedItem]); + }, [gl, camera, toggleView, activeModule, socket, selectedItem, setSelectedItem]); return ( <> diff --git a/app/src/modules/builder/wallAsset/wallAssetGroup.tsx b/app/src/modules/builder/wallAsset/wallAssetGroup.tsx index bae9f87..95e2e90 100644 --- a/app/src/modules/builder/wallAsset/wallAssetGroup.tsx +++ b/app/src/modules/builder/wallAsset/wallAssetGroup.tsx @@ -1,5 +1,5 @@ import { useEffect } from 'react'; -import { useActiveTool, useToggleView } from '../../../store/builder/store'; +import { useToggleView, useToolMode } from '../../../store/builder/store'; import { useBuilderStore } from '../../../store/builder/useBuilderStore'; import { useVersionContext } from '../version/versionContext'; import { useSceneContext } from '../../scene/sceneContext'; @@ -10,10 +10,10 @@ import WallAssetInstances from './Instances/wallAssetInstances' import { getWallAssetsApi } from '../../../services/factoryBuilder/asset/wallAsset/getWallAssetsApi'; function WallAssetGroup() { - const { togglView } = useToggleView(); - const { setSelectedFloorAsset, setDeletableWallAsset } = useBuilderStore(); + const { toggleView } = useToggleView(); + const { setSelectedWallAsset, setDeletableWallAsset } = useBuilderStore(); const { activeModule } = useModuleStore(); - const { activeTool } = useActiveTool(); + const { toolMode } = useToolMode(); const { selectedVersionStore } = useVersionContext(); const { selectedVersion } = selectedVersionStore(); const { wallAssetStore } = useSceneContext(); @@ -21,11 +21,11 @@ function WallAssetGroup() { const { projectId } = useParams(); useEffect(() => { - if (togglView || activeModule !== 'builder') { - setSelectedFloorAsset(null); + if (toggleView || activeModule !== 'builder' || toolMode !== 'cursor') { + setSelectedWallAsset(null); } setDeletableWallAsset(null); - }, [togglView, activeModule, activeTool]) + }, [toggleView, activeModule, toolMode]) useEffect(() => { if (projectId && selectedVersion) { diff --git a/app/src/modules/builder/zone/zoneGroup.tsx b/app/src/modules/builder/zone/zoneGroup.tsx index c44bf09..08d3e9c 100644 --- a/app/src/modules/builder/zone/zoneGroup.tsx +++ b/app/src/modules/builder/zone/zoneGroup.tsx @@ -11,7 +11,7 @@ import ZoneInstances from './Instances/zoneInstances'; import { getZonesApi } from '../../../services/factoryBuilder/zone/getZonesApi'; function ZoneGroup() { - const { togglView } = useToggleView(); + const { toggleView } = useToggleView(); const { setSelectedZone } = useBuilderStore(); const { activeModule } = useModuleStore(); const { activeTool } = useActiveTool(); @@ -22,10 +22,10 @@ function ZoneGroup() { const { projectId } = useParams(); useEffect(() => { - if (togglView || activeModule !== 'builder') { + if (toggleView || activeModule !== 'builder') { setSelectedZone(null); } - }, [togglView, activeModule, activeTool]) + }, [toggleView, activeModule, activeTool]) useEffect(() => { if (projectId && selectedVersion) { diff --git a/app/src/modules/scene/postProcessing/postProcessing.tsx b/app/src/modules/scene/postProcessing/postProcessing.tsx index 8aacd96..5558f9d 100644 --- a/app/src/modules/scene/postProcessing/postProcessing.tsx +++ b/app/src/modules/scene/postProcessing/postProcessing.tsx @@ -1,25 +1,19 @@ -import { DepthOfField, Bloom, EffectComposer, N8AO, Outline } from "@react-three/postprocessing"; -import { useThree } from "@react-three/fiber"; -import { BlendFunction } from "postprocessing"; -import { - useDeletableFloorItem, - useSelectedWallItem, - useSelectedFloorItem, -} from "../../../store/builder/store"; -import * as CONSTANTS from "../../../types/world/worldConstants"; -import { useDeletableEventSphere, useSelectedEventSphere, useSelectedPoints } from "../../../store/simulation/useSimulationStore"; import { useEffect } from "react"; +import { BlendFunction } from "postprocessing"; +import { DepthOfField, Bloom, EffectComposer, N8AO, Outline } from "@react-three/postprocessing"; +import { useDeletableFloorItem, useSelectedWallItem, useSelectedFloorItem, } from "../../../store/builder/store"; +import { useDeletableEventSphere, useSelectedEventSphere, useSelectedPoints } from "../../../store/simulation/useSimulationStore"; import { useBuilderStore } from "../../../store/builder/useBuilderStore"; +import * as CONSTANTS from "../../../types/world/worldConstants"; export default function PostProcessing() { - const { scene } = useThree(); const { selectedPoints } = useSelectedPoints(); const { deletableFloorItem } = useDeletableFloorItem(); const { selectedWallItem } = useSelectedWallItem(); const { selectedFloorItem } = useSelectedFloorItem(); const { selectedEventSphere } = useSelectedEventSphere(); const { deletableEventSphere } = useDeletableEventSphere(); - const { selectedAisle, selectedWall, selectedDecal, selectedFloor, selectedWallAsset, deletableWallAsset } = useBuilderStore(); + const { selectedAisle, selectedWall, selectedDecal, selectedFloor, selectedWallAsset, deletableWallAsset, deletableDecal } = useBuilderStore(); function flattenChildren(children: any[]) { const allChildren: any[] = []; @@ -68,6 +62,10 @@ export default function PostProcessing() { // console.log('selectedPoints: ', selectedPoints); }, [selectedPoints]) + useEffect(() => { + // console.log('deletableDecal: ', deletableDecal); + }, [deletableDecal]) + return ( )} + {deletableDecal && ( + + )} {deletableFloorItem && ( ((set: any, get: any) => ({ // }); // }, // })); + export const useLoadingProgress = create<{ loadingProgress: number; setLoadingProgress: (x: number) => void; diff --git a/app/src/store/builder/useBuilderStore.ts b/app/src/store/builder/useBuilderStore.ts index 5be4e3b..d6176f7 100644 --- a/app/src/store/builder/useBuilderStore.ts +++ b/app/src/store/builder/useBuilderStore.ts @@ -39,6 +39,7 @@ interface BuilderState { // Decal Settings selectedDecal: Object3D | null; + deletableDecal: Object3D | null; // Aisle General selectedAisle: Object3D | null; @@ -87,6 +88,7 @@ interface BuilderState { // Setters - Decal setSelectedDecal: (decal: Object3D | null) => void; + setDeletableDecal: (decal: Object3D | null) => void; // Setters - Aisle General setSelectedAisle: (aisle: Object3D | null) => void; @@ -140,6 +142,7 @@ export const useBuilderStore = create()( zoneColor: 'blue', selectedDecal: null, + deletableDecal: null, selectedAisle: null, aisleType: 'solid-aisle', @@ -293,6 +296,12 @@ export const useBuilderStore = create()( }) }, + setDeletableDecal: (decal: Object3D | null) => { + set((state) => { + state.deletableDecal = decal; + }) + }, + // === Setters: Aisle General === setSelectedAisle: (aisle: Object3D | null) => { diff --git a/app/src/store/builder/useFloorStore.ts b/app/src/store/builder/useFloorStore.ts index a045884..6f488a5 100644 --- a/app/src/store/builder/useFloorStore.ts +++ b/app/src/store/builder/useFloorStore.ts @@ -21,7 +21,7 @@ interface FloorStore { setMaterial: (uuid: string, sideMaterial: string, topMaterial: string) => void; addDecal: (floors: string, decal: Decal) => void; updateDecal: (decalUuid: string, decal: Decal) => void; - removeDecal: (decalUuid: string) => void; + removeDecal: (decalUuid: string) => Floor | undefined; updateDecalPosition: (decalUuid: string, position: [number, number, number]) => void; updateDecalRotation: (decalUuid: string, rotation: number) => void; updateDecalScale: (decalUuid: string, scale: number) => void; @@ -213,11 +213,19 @@ export const createFloorStore = () => { } }), - removeDecal: (decalUuid) => set(state => { - for (const floor of state.floors) { - floor.decals = floor.decals.filter(d => d.decalUuid !== decalUuid); - } - }), + removeDecal: (decalUuid) => { + let affectedFloor: Floor | undefined; + set(state => { + for (const floor of state.floors) { + const hasDecal = floor.decals.some(d => d.decalUuid === decalUuid); + if (hasDecal) { + floor.decals = floor.decals.filter(d => d.decalUuid !== decalUuid); + affectedFloor = JSON.parse(JSON.stringify(floor)); + } + } + }); + return affectedFloor; + }, updateDecalPosition: (decalUuid, position) => set(state => { for (const floor of state.floors) { diff --git a/app/src/store/builder/useWallStore.ts b/app/src/store/builder/useWallStore.ts index db40269..92b6fe5 100644 --- a/app/src/store/builder/useWallStore.ts +++ b/app/src/store/builder/useWallStore.ts @@ -11,7 +11,7 @@ interface WallStore { removeWallByPoints: (Points: [Point, Point]) => Wall | undefined; addDecal: (wallUuid: string, decal: Decal) => void; updateDecal: (decalUuid: string, decal: Decal) => void; - removeDecal: (decalUuid: string) => void; + removeDecal: (decalUuid: string) => Wall | undefined; updateDecalPosition: (decalUuid: string, position: [number, number, number]) => void; updateDecalRotation: (decalUuid: string, rotation: number) => void; updateDecalScale: (decalUuid: string, scale: number) => void; @@ -99,11 +99,19 @@ export const createWallStore = () => { } }), - removeDecal: (decalUuid) => set((state) => { - for (const wall of state.walls) { - wall.decals = wall.decals.filter(d => d.decalUuid !== decalUuid); - } - }), + removeDecal: (decalUuid) => { + let affectedWall: Wall | undefined; + set((state) => { + for (const wall of state.walls) { + const hasDecal = wall.decals.some(d => d.decalUuid === decalUuid); + if (hasDecal) { + wall.decals = wall.decals.filter(d => d.decalUuid !== decalUuid); + affectedWall = JSON.parse(JSON.stringify(wall)); + } + } + }); + return affectedWall; + }, updateDecalPosition: (decalUuid, position) => set((state) => { for (const wall of state.walls) { From 98be35bf5fa41d7b1bb26475a2c955b8bbf65f3d Mon Sep 17 00:00:00 2001 From: Poovizhi Date: Tue, 26 Aug 2025 15:36:07 +0530 Subject: [PATCH 10/34] included decal-list --- .../components/layout/sidebarLeft/Assets.tsx | 158 +++++++++++++----- .../asset/decals/getCategoryDecals.ts | 20 +++ 2 files changed, 134 insertions(+), 44 deletions(-) create mode 100644 app/src/services/factoryBuilder/asset/decals/getCategoryDecals.ts diff --git a/app/src/components/layout/sidebarLeft/Assets.tsx b/app/src/components/layout/sidebarLeft/Assets.tsx index c08ef78..4e946d3 100644 --- a/app/src/components/layout/sidebarLeft/Assets.tsx +++ b/app/src/components/layout/sidebarLeft/Assets.tsx @@ -21,6 +21,8 @@ import { HangTagIcon, NavigationIcon, } from "../../icons/ExportCommonIcons"; +import { assert } from "console"; +import { getCategoryDecals } from "../../../services/factoryBuilder/asset/decals/getCategoryDecals"; // ------------------------------------- interface AssetProp { @@ -47,6 +49,7 @@ const Assets: React.FC = () => { const [searchValue, setSearchValue] = useState(""); const [selectedCategory, setSelectedCategory] = useState(null); const [categoryAssets, setCategoryAssets] = useState([]); + const [filtereredAssets, setFiltereredAssets] = useState([]); const [categoryList, setCategoryList] = useState([]); const [isLoading, setisLoading] = useState(false); // Loading state for assets @@ -118,6 +121,20 @@ const Assets: React.FC = () => { setisLoading(false); } }; + const fetchCategoryDecals = async (asset: any) => { + setisLoading(true); + // setSelectedCategory(asset); + try { + const res = await getCategoryDecals(asset); + setCategoryAssets(res); + setFiltereredAssets(res); + setisLoading(false); // End loading + // eslint-disable-next-line + } catch (error) { + echo.error("failed to fetch assets"); + setisLoading(false); + } + }; const activeSubcategories = [ { name: "Safety", icon: }, @@ -127,6 +144,7 @@ const Assets: React.FC = () => { ]; const { selectedSubCategory, setSelectedSubCategory } = useDecalStore(); + return (
@@ -211,7 +229,10 @@ const Assets: React.FC = () => { className={`catogory-asset-filter-wrapper ${ selectedSubCategory === cat.name ? "active" : "" }`} - onClick={() => setSelectedSubCategory(cat.name)} + onClick={() => { + fetchCategoryDecals(cat.name); + setSelectedSubCategory(cat.name); + }} >
{cat.icon}
{cat.name}
@@ -220,49 +241,96 @@ const Assets: React.FC = () => {
)} -
- {categoryAssets?.map((asset: any, index: number) => ( -
- {asset.filename} { - setSelectedItem({ - name: asset.filename, - id: asset.AssetID, - type: - asset.type === "undefined" - ? undefined - : asset.type, - category: asset.category, - subType: asset.subType, - }); - }} - /> -
- {asset.filename - .split("_") - .map( - (word: any) => - word.charAt(0).toUpperCase() + word.slice(1) - ) - .join(" ")} + + {selectedCategory !== "Decals" && !selectedSubCategory ? ( +
+ {categoryAssets?.map((asset: any, index: number) => ( +
+ {asset.filename} { + setSelectedItem({ + name: asset.filename, + id: asset.AssetID, + type: + asset.type === "undefined" + ? undefined + : asset.type, + category: asset.category, + subType: asset.subType, + }); + }} + /> +
+ {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! -
- )} -
+ ))} + {categoryAssets.length === 0 && ( +
+ 🚧 The asset shelf is empty. We're working on filling + it up! +
+ )} +
+ ) : ( +
+ {categoryAssets?.map((asset: any, index: number) => ( +
+ {asset.decalName} { + setSelectedItem({ + name: asset.decalName, + id: asset.id, + type: + asset.type === "undefined" + ? undefined + : asset.type, + category: asset.category, + // subType: asset.subType, + }); + }} + /> +
+ {asset.decalName + .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! +
+ )} +
+ )}
); } @@ -282,7 +350,9 @@ const Assets: React.FC = () => { key={`${index}-${category}`} className="category" id={category} - onClick={() => fetchCategoryAssets(category)} + onClick={() => { + fetchCategoryAssets(category); + }} > { + try { + const response = await fetch( + `${BackEnd_url}/api/v1/categoryDecalDatas/${category}`, + { + method: "GET", + headers: { + "Content-Type": "application/json", + }, + } + ); + + const result = await response.json(); + return result; + } catch (error: any) { + echo.error("Failed to get category asset"); + console.log(error.message); + } +}; \ No newline at end of file From 165325468a819e5fba7b5ef62b6e96c210e13af5 Mon Sep 17 00:00:00 2001 From: Jerald-Golden-B Date: Tue, 26 Aug 2025 15:50:02 +0530 Subject: [PATCH 11/34] added ui fro and iintegerated ui for decal modification --- .../customInput/RotationInput.tsx | 31 ++-- .../properties/SelectedDecalProperties.tsx | 154 +++++++++++++++++- .../modules/builder/Decal/decalInstance.tsx | 8 +- .../builder/wall/Instances/instance/wall.tsx | 3 +- .../scene/postProcessing/postProcessing.tsx | 2 +- app/src/store/builder/useBuilderStore.ts | 6 +- app/src/store/builder/useFloorStore.ts | 23 ++- app/src/store/builder/useWallStore.ts | 21 ++- app/src/types/builderTypes.d.ts | 1 + 9 files changed, 204 insertions(+), 45 deletions(-) diff --git a/app/src/components/layout/sidebarRight/customInput/RotationInput.tsx b/app/src/components/layout/sidebarRight/customInput/RotationInput.tsx index ccddafa..1265d81 100644 --- a/app/src/components/layout/sidebarRight/customInput/RotationInput.tsx +++ b/app/src/components/layout/sidebarRight/customInput/RotationInput.tsx @@ -1,23 +1,29 @@ import React from "react"; interface RotationInputProps { - heading?: string; // Optional label for the input - label?: string; // Optional label for the input - onChange: (value: string) => void; // Callback for value change - placeholder?: string; // Optional placeholder - type?: string; // Input type (e.g., text, number, email) + heading?: string; + label?: string; + onChange: (value: string) => void; + placeholder?: string; + type?: string; value?: number; - disabled?: boolean; // Disable the input if true + disabled?: boolean; + min?: number; + max?: number; + step?: number; } const RotationInput: React.FC = ({ - label = "Rotate :", // Default label - heading = "Rotation", // Default heading + label = "Rotate :", + heading = "Rotation", onChange, - placeholder = "Enter value", // Default placeholder - type = "number", // Default type - value = "number", + placeholder = "Enter value", + type = "number", + value, disabled = false, + min, + max, + step, }) => { return (
@@ -32,6 +38,9 @@ const RotationInput: React.FC = ({ placeholder={placeholder} value={value} disabled={disabled} + min={min} + max={max} + step={step} />
diff --git a/app/src/components/layout/sidebarRight/properties/SelectedDecalProperties.tsx b/app/src/components/layout/sidebarRight/properties/SelectedDecalProperties.tsx index 1db536b..7994a1f 100644 --- a/app/src/components/layout/sidebarRight/properties/SelectedDecalProperties.tsx +++ b/app/src/components/layout/sidebarRight/properties/SelectedDecalProperties.tsx @@ -1,42 +1,178 @@ +import { useParams } from "react-router-dom"; +import { useVersionContext } from "../../../../modules/builder/version/versionContext"; +import { useSceneContext } from "../../../../modules/scene/sceneContext"; +import { useBuilderStore } from "../../../../store/builder/useBuilderStore"; import { LayeringBottomIcon, LayeringTopIcon } from "../../../icons/ExportCommonIcons"; +import { useSocketStore } from "../../../../store/builder/store"; import InputRange from "../../../ui/inputs/InputRange"; import RotationInput from "../customInput/RotationInput"; +import { getUserData } from "../../../../functions/getUserData"; +// import { upsertWallApi } from "../../../../services/factoryBuilder/wall/upsertWallApi"; +// import { upsertFloorApi } from "../../../../services/factoryBuilder/floor/upsertFloorApi"; + const SelectedDecalProperties = () => { + const { selectedDecal, setSelectedDecal } = useBuilderStore(); + const { wallStore, floorStore } = useSceneContext(); + const { updateDecal: updateDecalFromWall } = wallStore(); + const { updateDecal: updateDecalFromFloor } = floorStore(); + const { userId, organization } = getUserData(); + const { selectedVersionStore } = useVersionContext(); + const { selectedVersion } = selectedVersionStore(); + const { projectId } = useParams(); + const { socket } = useSocketStore(); + + const updateBackend = (updatedData: Wall | Floor) => { + if ('wallUuid' in updatedData) { + if (projectId && updatedData) { + // API + + // upsertWallApi(projectId, selectedVersion?.versionId || '', updatedData); + + // SOCKET + + const data = { + wallData: updatedData, + projectId: projectId, + versionId: selectedVersion?.versionId || '', + userId: userId, + organization: organization + } + + socket.emit('v1:model-Wall:add', data); + } + } else if ('floorUuid' in updatedData) { + if (projectId && updatedData) { + // API + + // upsertFloorApi(projectId, selectedVersion?.versionId || '', updatedData); + + // SOCKET + + const data = { + floorData: updatedData, + projectId: projectId, + versionId: selectedVersion?.versionId || '', + userId: userId, + organization: organization + } + + socket.emit('v1:model-Floor:add', data); + } + } + } + + const handleRotationChange = (value: number) => { + if (!selectedDecal) return; + const updatedDecal = { ...selectedDecal.decalData, decalRotation: value }; + setSelectedDecal({ ...selectedDecal, decalData: updatedDecal }); + + if ('wallUuid' in selectedDecal.decalData.decalType) { + const updatedWall = updateDecalFromWall(updatedDecal.decalUuid, updatedDecal); + if (updatedWall) updateBackend(updatedWall); + } else if ('floorUuid' in selectedDecal.decalData.decalType) { + const updatedFloor = updateDecalFromFloor(updatedDecal.decalUuid, updatedDecal); + if (updatedFloor) updateBackend(updatedFloor); + } + } + + const handleScaleChange = (value: number) => { + if (!selectedDecal) return; + const updatedDecal = { ...selectedDecal.decalData, decalScale: value }; + setSelectedDecal({ ...selectedDecal, decalData: updatedDecal }); + + if ('wallUuid' in selectedDecal.decalData.decalType) { + const updatedWall = updateDecalFromWall(updatedDecal.decalUuid, updatedDecal); + if (updatedWall) updateBackend(updatedWall); + } else if ('floorUuid' in selectedDecal.decalData.decalType) { + const updatedFloor = updateDecalFromFloor(updatedDecal.decalUuid, updatedDecal); + if (updatedFloor) updateBackend(updatedFloor); + } + } + + const handleOpacityChange = (value: number) => { + if (!selectedDecal) return; + const updatedDecal = { ...selectedDecal.decalData, decalOpacity: value }; + setSelectedDecal({ ...selectedDecal, decalData: updatedDecal }); + + if ('wallUuid' in selectedDecal.decalData.decalType) { + const updatedWall = updateDecalFromWall(updatedDecal.decalUuid, updatedDecal); + if (updatedWall) updateBackend(updatedWall); + } else if ('floorUuid' in selectedDecal.decalData.decalType) { + const updatedFloor = updateDecalFromFloor(updatedDecal.decalUuid, updatedDecal); + if (updatedFloor) updateBackend(updatedFloor); + } + } + + const handleLayerChange = (direction: "up" | "down") => { + if (!selectedDecal) return; + + const position: [number, number, number] = [...(selectedDecal.decalData.decalPosition || [0, 0, 0]),]; + + if (direction === "up") { + position[2] = Math.abs(position[2]); + } else { + position[2] = -Math.abs(position[2]); + } + + const updatedDecal: Decal = { ...selectedDecal.decalData, decalPosition: position, }; + + setSelectedDecal({ ...selectedDecal, decalData: updatedDecal }); + + if ("wallUuid" in selectedDecal.decalData.decalType) { + const updatedWall = updateDecalFromWall(updatedDecal.decalUuid, updatedDecal); + if (updatedWall) updateBackend(updatedWall); + } else if ("floorUuid" in selectedDecal.decalData.decalType) { + const updatedFloor = updateDecalFromFloor(updatedDecal.decalUuid, updatedDecal); + if (updatedFloor) updateBackend(updatedFloor); + } + }; + + if (!selectedDecal) return null; + return (
Decal Properties
{ }} - value={10} + onChange={(e) => { handleRotationChange(parseFloat(e)) }} + value={selectedDecal.decalData.decalRotation || 0} /> { }} - value={10} + onChange={(e) => { handleScaleChange(parseFloat(e)) }} + value={selectedDecal.decalData.decalScale || 1} />
console.log(value)} + onChange={(value: number) => handleOpacityChange(value)} />
Layering
- -
diff --git a/app/src/modules/builder/Decal/decalInstance.tsx b/app/src/modules/builder/Decal/decalInstance.tsx index 69b0c7c..59f2141 100644 --- a/app/src/modules/builder/Decal/decalInstance.tsx +++ b/app/src/modules/builder/Decal/decalInstance.tsx @@ -93,7 +93,7 @@ function DecalInstance({ parent, visible = true, decal, zPosition = decal.decalP // debug visible={visible} position={[decal.decalPosition[0], decal.decalPosition[1], zPosition]} - rotation={[0, 0, 0]} + rotation={[0, 0, decal.decalRotation * (Math.PI / 180)]} scale={[decal.decalScale, decal.decalScale, 0.01]} userData={decal} onClick={(e) => { @@ -101,7 +101,7 @@ function DecalInstance({ parent, visible = true, decal, zPosition = decal.decalP if (e.object.userData.decalUuid) { e.stopPropagation(); if (toolMode === 'cursor') { - setSelectedDecal(e.object); + setSelectedDecal({ decalMesh: e.object, decalData: decal }); setSelectedWall(null); setSelectedFloor(null); } else if (toolMode === '3D-Delete') { @@ -131,7 +131,7 @@ function DecalInstance({ parent, visible = true, decal, zPosition = decal.decalP } }} onPointerMissed={() => { - if (selectedDecal && selectedDecal.userData.decalUuid === decal.decalUuid) { + if (selectedDecal && selectedDecal.decalMesh.userData.decalUuid === decal.decalUuid) { setSelectedDecal(null); } }} @@ -141,6 +141,8 @@ function DecalInstance({ parent, visible = true, decal, zPosition = decal.decalP side={THREE.DoubleSide} polygonOffset polygonOffsetFactor={-1} + transparent + opacity={decal.decalOpacity} /> ) diff --git a/app/src/modules/builder/wall/Instances/instance/wall.tsx b/app/src/modules/builder/wall/Instances/instance/wall.tsx index 939dc78..50d3536 100644 --- a/app/src/modules/builder/wall/Instances/instance/wall.tsx +++ b/app/src/modules/builder/wall/Instances/instance/wall.tsx @@ -167,6 +167,7 @@ function Wall({ wall }: { readonly wall: Wall }) { decalId: 'Default Decal', decalPosition: [0, 0, wall.wallThickness / 2 + 0.001], decalRotation: 0, + decalOpacity: 1, decalScale: 1, decalType: { type: 'Wall', wallUuid: wall.wallUuid } } @@ -183,7 +184,7 @@ function Wall({ wall }: { readonly wall: Wall }) { {wall.decals.map((decal) => ( - + ))} diff --git a/app/src/modules/scene/postProcessing/postProcessing.tsx b/app/src/modules/scene/postProcessing/postProcessing.tsx index 5558f9d..555c7a3 100644 --- a/app/src/modules/scene/postProcessing/postProcessing.tsx +++ b/app/src/modules/scene/postProcessing/postProcessing.tsx @@ -165,7 +165,7 @@ export default function PostProcessing() { )} {selectedDecal && ( void; // Setters - Decal - setSelectedDecal: (decal: Object3D | null) => void; + setSelectedDecal: (decal: { decalMesh: Object3D, decalData: Decal } | null) => void; setDeletableDecal: (decal: Object3D | null) => void; // Setters - Aisle General @@ -290,7 +290,7 @@ export const useBuilderStore = create()( // === Setters: Decal === - setSelectedDecal: (decal: Object3D | null) => { + setSelectedDecal: (decal: { decalMesh: Object3D, decalData: Decal } | null) => { set((state) => { state.selectedDecal = decal; }) diff --git a/app/src/store/builder/useFloorStore.ts b/app/src/store/builder/useFloorStore.ts index 6f488a5..c7b9545 100644 --- a/app/src/store/builder/useFloorStore.ts +++ b/app/src/store/builder/useFloorStore.ts @@ -20,7 +20,7 @@ interface FloorStore { setDepth: (uuid: string, depth: number) => void; setMaterial: (uuid: string, sideMaterial: string, topMaterial: string) => void; addDecal: (floors: string, decal: Decal) => void; - updateDecal: (decalUuid: string, decal: Decal) => void; + updateDecal: (decalUuid: string, decal: Decal) => Floor | undefined; removeDecal: (decalUuid: string) => Floor | undefined; updateDecalPosition: (decalUuid: string, position: [number, number, number]) => void; updateDecalRotation: (decalUuid: string, rotation: number) => void; @@ -203,15 +203,20 @@ export const createFloorStore = () => { } }), - updateDecal: (decalUuid, updatedDecal) => set(state => { - for (const floor of state.floors) { - const index = floor.decals.findIndex(d => d.decalUuid === decalUuid); - if (index !== -1) { - floor.decals[index] = updatedDecal; - break; + updateDecal: (decalUuid, updatedDecal) => { + let affectedFloor: Floor | undefined; + set(state => { + for (const floor of state.floors) { + const index = floor.decals.findIndex(d => d.decalUuid === decalUuid); + if (index !== -1) { + floor.decals[index] = updatedDecal; + affectedFloor = JSON.parse(JSON.stringify(floor)); + break; + } } - } - }), + }) + return affectedFloor; + }, removeDecal: (decalUuid) => { let affectedFloor: Floor | undefined; diff --git a/app/src/store/builder/useWallStore.ts b/app/src/store/builder/useWallStore.ts index 92b6fe5..d1b2e86 100644 --- a/app/src/store/builder/useWallStore.ts +++ b/app/src/store/builder/useWallStore.ts @@ -10,7 +10,7 @@ interface WallStore { clearWalls: () => void; removeWallByPoints: (Points: [Point, Point]) => Wall | undefined; addDecal: (wallUuid: string, decal: Decal) => void; - updateDecal: (decalUuid: string, decal: Decal) => void; + updateDecal: (decalUuid: string, decal: Decal) => Wall | undefined; removeDecal: (decalUuid: string) => Wall | undefined; updateDecalPosition: (decalUuid: string, position: [number, number, number]) => void; updateDecalRotation: (decalUuid: string, rotation: number) => void; @@ -90,14 +90,19 @@ export const createWallStore = () => { } }), - updateDecal: (decalUuid, decal) => set((state) => { - for (const wall of state.walls) { - const decalToUpdate = wall.decals.find(d => d.decalUuid === decalUuid); - if (decalToUpdate) { - Object.assign(decalToUpdate, decal); + updateDecal: (decalUuid, decal) => { + let affectedWall: Wall | undefined; + set((state) => { + for (const wall of state.walls) { + const decalToUpdate = wall.decals.find(d => d.decalUuid === decalUuid); + if (decalToUpdate) { + Object.assign(decalToUpdate, decal); + affectedWall = JSON.parse(JSON.stringify(wall)); + } } - } - }), + }); + return affectedWall; + }, removeDecal: (decalUuid) => { let affectedWall: Wall | undefined; diff --git a/app/src/types/builderTypes.d.ts b/app/src/types/builderTypes.d.ts index 3487fc6..e0440ec 100644 --- a/app/src/types/builderTypes.d.ts +++ b/app/src/types/builderTypes.d.ts @@ -88,6 +88,7 @@ interface Decal { decalType: WallDecal | FloorDecal; decalPosition: [number, number, number]; decalRotation: number; + decalOpacity: number; decalScale: number; } From c0e040fb3ab9e25f1652e392e06e1911e5236602 Mon Sep 17 00:00:00 2001 From: Jerald-Golden-B Date: Tue, 26 Aug 2025 16:38:29 +0530 Subject: [PATCH 12/34] added decal movement across the wall --- .../properties/SelectedDecalProperties.tsx | 20 ++-- .../modules/builder/Decal/decalInstance.tsx | 104 ++++++++++++++++-- .../calculateAssetTransformationOnWall.ts | 4 +- .../Instances/Instance/wallAssetInstance.tsx | 35 ++---- app/src/store/builder/useWallStore.ts | 23 ++-- 5 files changed, 131 insertions(+), 55 deletions(-) diff --git a/app/src/components/layout/sidebarRight/properties/SelectedDecalProperties.tsx b/app/src/components/layout/sidebarRight/properties/SelectedDecalProperties.tsx index 7994a1f..99c16da 100644 --- a/app/src/components/layout/sidebarRight/properties/SelectedDecalProperties.tsx +++ b/app/src/components/layout/sidebarRight/properties/SelectedDecalProperties.tsx @@ -14,8 +14,8 @@ import { getUserData } from "../../../../functions/getUserData"; const SelectedDecalProperties = () => { const { selectedDecal, setSelectedDecal } = useBuilderStore(); const { wallStore, floorStore } = useSceneContext(); - const { updateDecal: updateDecalFromWall } = wallStore(); - const { updateDecal: updateDecalFromFloor } = floorStore(); + const { updateDecal: updateDecalInWall } = wallStore(); + const { updateDecal: updateDecalInFloor } = floorStore(); const { userId, organization } = getUserData(); const { selectedVersionStore } = useVersionContext(); const { selectedVersion } = selectedVersionStore(); @@ -68,10 +68,10 @@ const SelectedDecalProperties = () => { setSelectedDecal({ ...selectedDecal, decalData: updatedDecal }); if ('wallUuid' in selectedDecal.decalData.decalType) { - const updatedWall = updateDecalFromWall(updatedDecal.decalUuid, updatedDecal); + const updatedWall = updateDecalInWall(updatedDecal.decalUuid, updatedDecal); if (updatedWall) updateBackend(updatedWall); } else if ('floorUuid' in selectedDecal.decalData.decalType) { - const updatedFloor = updateDecalFromFloor(updatedDecal.decalUuid, updatedDecal); + const updatedFloor = updateDecalInFloor(updatedDecal.decalUuid, updatedDecal); if (updatedFloor) updateBackend(updatedFloor); } } @@ -82,10 +82,10 @@ const SelectedDecalProperties = () => { setSelectedDecal({ ...selectedDecal, decalData: updatedDecal }); if ('wallUuid' in selectedDecal.decalData.decalType) { - const updatedWall = updateDecalFromWall(updatedDecal.decalUuid, updatedDecal); + const updatedWall = updateDecalInWall(updatedDecal.decalUuid, updatedDecal); if (updatedWall) updateBackend(updatedWall); } else if ('floorUuid' in selectedDecal.decalData.decalType) { - const updatedFloor = updateDecalFromFloor(updatedDecal.decalUuid, updatedDecal); + const updatedFloor = updateDecalInFloor(updatedDecal.decalUuid, updatedDecal); if (updatedFloor) updateBackend(updatedFloor); } } @@ -96,10 +96,10 @@ const SelectedDecalProperties = () => { setSelectedDecal({ ...selectedDecal, decalData: updatedDecal }); if ('wallUuid' in selectedDecal.decalData.decalType) { - const updatedWall = updateDecalFromWall(updatedDecal.decalUuid, updatedDecal); + const updatedWall = updateDecalInWall(updatedDecal.decalUuid, updatedDecal); if (updatedWall) updateBackend(updatedWall); } else if ('floorUuid' in selectedDecal.decalData.decalType) { - const updatedFloor = updateDecalFromFloor(updatedDecal.decalUuid, updatedDecal); + const updatedFloor = updateDecalInFloor(updatedDecal.decalUuid, updatedDecal); if (updatedFloor) updateBackend(updatedFloor); } } @@ -120,10 +120,10 @@ const SelectedDecalProperties = () => { setSelectedDecal({ ...selectedDecal, decalData: updatedDecal }); if ("wallUuid" in selectedDecal.decalData.decalType) { - const updatedWall = updateDecalFromWall(updatedDecal.decalUuid, updatedDecal); + const updatedWall = updateDecalInWall(updatedDecal.decalUuid, updatedDecal); if (updatedWall) updateBackend(updatedWall); } else if ("floorUuid" in selectedDecal.decalData.decalType) { - const updatedFloor = updateDecalFromFloor(updatedDecal.decalUuid, updatedDecal); + const updatedFloor = updateDecalInFloor(updatedDecal.decalUuid, updatedDecal); if (updatedFloor) updateBackend(updatedFloor); } }; diff --git a/app/src/modules/builder/Decal/decalInstance.tsx b/app/src/modules/builder/Decal/decalInstance.tsx index 59f2141..d414419 100644 --- a/app/src/modules/builder/Decal/decalInstance.tsx +++ b/app/src/modules/builder/Decal/decalInstance.tsx @@ -1,13 +1,13 @@ import * as THREE from 'three'; -import { Decal } from '@react-three/drei' -import { useLoader } from '@react-three/fiber'; +import { CameraControls, Decal } from '@react-three/drei' +import { useLoader, useThree } from '@react-three/fiber'; import { useSocketStore, useToggleView, useToolMode } from '../../../store/builder/store'; import { useBuilderStore } from '../../../store/builder/useBuilderStore'; import defaultMaterial from '../../../assets/textures/floor/wall-tex.png'; import useModuleStore from '../../../store/useModuleStore'; import { useSceneContext } from '../../scene/sceneContext'; -import { useEffect } from 'react'; +import { useEffect, useRef } from 'react'; import { getUserData } from '../../../functions/getUserData'; import { useVersionContext } from '../version/versionContext'; import { useParams } from 'react-router-dom'; @@ -18,8 +18,8 @@ import { useParams } from 'react-router-dom'; function DecalInstance({ parent, visible = true, decal, zPosition = decal.decalPosition[2] }: { parent: Wall | Floor; visible?: boolean, decal: Decal, zPosition?: number }) { const { setSelectedWall, setSelectedFloor, selectedDecal, deletableDecal, setSelectedDecal, setDeletableDecal } = useBuilderStore(); const { wallStore, floorStore } = useSceneContext(); - const { removeDecal: removeDecalFromWall } = wallStore(); - const { removeDecal: removeDecalFromFloor } = floorStore(); + const { removeDecal: removeDecalInWall, updateDecalPosition: updateDecalPositionInWall, getWallById } = wallStore(); + const { removeDecal: removeDecalInFloor } = floorStore(); const { toolMode } = useToolMode(); const { toggleView } = useToggleView(); const { activeModule } = useModuleStore(); @@ -30,6 +30,10 @@ function DecalInstance({ parent, visible = true, decal, zPosition = decal.decalP const { socket } = useSocketStore(); const material = useLoader(THREE.TextureLoader, defaultMaterial); + const { raycaster, pointer, camera, scene, gl, controls } = useThree(); + const isDraggingRef = useRef(false); + const dragOffsetRef = useRef(null); + useEffect(() => { if (!toggleView && activeModule === 'builder') { if (toolMode !== 'cursor') { @@ -44,9 +48,77 @@ function DecalInstance({ parent, visible = true, decal, zPosition = decal.decalP } }, [toggleView, toolMode, activeModule, selectedDecal, deletableDecal]); + useEffect(() => { + const canvasElement = gl.domElement; + + const handlePointerMove = (e: PointerEvent) => { + if (!isDraggingRef.current || !selectedDecal || selectedDecal.decalData.decalUuid !== decal.decalUuid) return; + + raycaster.setFromCamera(pointer, camera); + const intersects = raycaster.intersectObjects(scene.children, true); + + const wallIntersect = intersects.find(i => i.object.userData && 'wallUuid' in parent && i.object.userData.wallUuid === parent.wallUuid); + + if (wallIntersect) { + const point = wallIntersect.object.worldToLocal(wallIntersect.point.clone()); + + let offset = dragOffsetRef.current || new THREE.Vector3(0, 0, 0); + + updateDecalPositionInWall(decal.decalUuid, [point.x + offset.x, point.y + offset.y, decal.decalPosition[2]]); + } + }; + + const handlePointerUp = (e: PointerEvent) => { + if (controls) { + (controls as CameraControls).enabled = true; + } + if (isDraggingRef.current) { + isDraggingRef.current = false; + dragOffsetRef.current = null; + + if ('wallUuid' in parent) { + setTimeout(() => { + const updatedWall = getWallById(parent.wallUuid); + + if (updatedWall) { + if (projectId && updatedWall) { + // API + + // upsertWallApi(projectId, selectedVersion?.versionId || '', updatedWall); + + // SOCKET + + const data = { + wallData: updatedWall, + projectId: projectId, + versionId: selectedVersion?.versionId || '', + userId: userId, + organization: organization + } + + socket.emit('v1:model-Wall:add', data); + } + } + }, 0) + } + + } + }; + + if (activeModule === 'builder' && !toggleView) { + canvasElement.addEventListener('pointermove', handlePointerMove); + canvasElement.addEventListener('pointerup', handlePointerUp); + } + + return () => { + canvasElement.removeEventListener('pointermove', handlePointerMove); + canvasElement.removeEventListener('pointerup', handlePointerUp); + }; + }, [gl, camera, scene, raycaster, selectedDecal, decal, parent, activeModule, toggleView, projectId, selectedVersion, userId, organization, socket]); + const deleteDecal = (decalUuid: string, parent: Wall | Floor) => { if ('wallUuid' in parent) { - const updatedWall = removeDecalFromWall(decalUuid); + const updatedWall = removeDecalInWall(decalUuid); if (projectId && updatedWall) { // API @@ -66,7 +138,7 @@ function DecalInstance({ parent, visible = true, decal, zPosition = decal.decalP socket.emit('v1:model-Wall:add', data); } } else if ('floorUuid' in parent) { - const updatedFloor = removeDecalFromFloor(decalUuid); + const updatedFloor = removeDecalInFloor(decalUuid); if (projectId && updatedFloor) { // API @@ -96,6 +168,24 @@ function DecalInstance({ parent, visible = true, decal, zPosition = decal.decalP rotation={[0, 0, decal.decalRotation * (Math.PI / 180)]} scale={[decal.decalScale, decal.decalScale, 0.01]} userData={decal} + onPointerDown={(e) => { + if (visible && !toggleView && activeModule === 'builder') { + if (e.object.userData.decalUuid && toolMode === 'cursor') { + e.stopPropagation(); + isDraggingRef.current = true; + if (controls) { + (controls as CameraControls).enabled = false; + } + setSelectedDecal({ decalMesh: e.object, decalData: decal }); + setSelectedWall(null); + setSelectedFloor(null); + + const localIntersect = e.object.worldToLocal(e.point.clone()); + dragOffsetRef.current = new THREE.Vector3(decal.decalPosition[0] - localIntersect.x, decal.decalPosition[1] - localIntersect.y, 0); + } + } + }} + onClick={(e) => { if (visible && !toggleView && activeModule === 'builder') { if (e.object.userData.decalUuid) { diff --git a/app/src/modules/builder/wallAsset/Instances/Instance/functions/calculateAssetTransformationOnWall.ts b/app/src/modules/builder/wallAsset/Instances/Instance/functions/calculateAssetTransformationOnWall.ts index aa881c8..89142d5 100644 --- a/app/src/modules/builder/wallAsset/Instances/Instance/functions/calculateAssetTransformationOnWall.ts +++ b/app/src/modules/builder/wallAsset/Instances/Instance/functions/calculateAssetTransformationOnWall.ts @@ -18,10 +18,10 @@ const calculateAssetTransformationOnWall = ( const projection = initialWallNormalized.clone().multiplyScalar(dotProduct); const perpendicular = new THREE.Vector3().subVectors(assetVector, projection); - const distanceFromWall = perpendicular.length(); + const distanceInWall = perpendicular.length(); const crossProduct = new THREE.Vector3().crossVectors(initialWallNormalized, perpendicular).y; - const signedDistance = distanceFromWall * (crossProduct >= 0 ? 1 : -1); + const signedDistance = distanceInWall * (crossProduct >= 0 ? 1 : -1); const percentage = Math.max(0, Math.min(1, dotProduct / initialWallLength)); diff --git a/app/src/modules/builder/wallAsset/Instances/Instance/wallAssetInstance.tsx b/app/src/modules/builder/wallAsset/Instances/Instance/wallAssetInstance.tsx index 76b3ad7..3919f65 100644 --- a/app/src/modules/builder/wallAsset/Instances/Instance/wallAssetInstance.tsx +++ b/app/src/modules/builder/wallAsset/Instances/Instance/wallAssetInstance.tsx @@ -23,7 +23,7 @@ function WallAssetInstance({ wallAsset }: { wallAsset: WallAsset }) { const { raycaster, pointer, camera, scene, controls, gl } = useThree(); const { wallStore, wallAssetStore } = useSceneContext(); const { walls, getWallById } = wallStore(); - const { updateWallAsset, removeWallAsset } = wallAssetStore(); + const { updateWallAsset, removeWallAsset, getWallAssetById } = wallAssetStore(); const { toggleView } = useToggleView(); const { activeTool } = useActiveTool(); const { activeModule } = useModuleStore(); @@ -116,33 +116,14 @@ function WallAssetInstance({ wallAsset }: { wallAsset: WallAsset }) { const canvasElement = gl.domElement; const onPointerUp = (e: PointerEvent) => { - draggingRef.current = false; - if (controls) { - (controls as any).enabled = true; - } + if (draggingRef.current) { + draggingRef.current = false; + if (controls) { + (controls as any).enabled = true; + } - if (selectedWallAsset) { - pointer.x = (e.clientX / window.innerWidth) * 2 - 1; - pointer.y = -(e.clientY / window.innerHeight) * 2 + 1; - - raycaster.setFromCamera(pointer, camera); - const intersects = raycaster.intersectObjects(scene.children, true); - const intersect = intersects.find((i: any) => i.object.name.includes('WallReference')); - - if (intersect && intersect.object.userData.wallUuid && selectedWallAsset.userData.modelUuid === wallAsset.modelUuid) { - const newPoint = closestPointOnLineSegment( - new THREE.Vector3(intersect.point.x, 0, intersect.point.z), - new THREE.Vector3(...intersect.object.userData.points[0].position), - new THREE.Vector3(...intersect.object.userData.points[1].position) - ); - - const wallRotation = intersect.object.rotation.clone(); - - const updatedWallAsset = updateWallAsset(wallAsset.modelUuid, { - wallUuid: intersect.object.userData.wallUuid, - position: [newPoint.x, wallAsset.wallAssetType === 'fixedMove' ? 0 : intersect.point.y, newPoint.z], - rotation: [wallRotation.x, wallRotation.y, wallRotation.z], - }); + if (selectedWallAsset) { + const updatedWallAsset = getWallAssetById(wallAsset.modelUuid); if (projectId && updatedWallAsset) { diff --git a/app/src/store/builder/useWallStore.ts b/app/src/store/builder/useWallStore.ts index d1b2e86..c7c07e9 100644 --- a/app/src/store/builder/useWallStore.ts +++ b/app/src/store/builder/useWallStore.ts @@ -12,7 +12,7 @@ interface WallStore { addDecal: (wallUuid: string, decal: Decal) => void; updateDecal: (decalUuid: string, decal: Decal) => Wall | undefined; removeDecal: (decalUuid: string) => Wall | undefined; - updateDecalPosition: (decalUuid: string, position: [number, number, number]) => void; + updateDecalPosition: (decalUuid: string, position: [number, number, number]) => Wall | undefined; updateDecalRotation: (decalUuid: string, rotation: number) => void; updateDecalScale: (decalUuid: string, scale: number) => void; @@ -118,15 +118,20 @@ export const createWallStore = () => { return affectedWall; }, - updateDecalPosition: (decalUuid, position) => set((state) => { - for (const wall of state.walls) { - const decal = wall.decals.find(d => d.decalUuid === decalUuid); - if (decal) { - decal.decalPosition = position; - break; + updateDecalPosition: (decalUuid, position) => { + let affectedWall: Wall | undefined; + set((state) => { + for (const wall of state.walls) { + const decal = wall.decals.find(d => d.decalUuid === decalUuid); + if (decal) { + decal.decalPosition = position; + affectedWall = JSON.parse(JSON.stringify(wall)); + break; + } } - } - }), + }) + return affectedWall; + }, updateDecalRotation: (decalUuid, rotation) => set((state) => { for (const wall of state.walls) { From 441f1efb230f14dd57bbe2866471458de6265e90 Mon Sep 17 00:00:00 2001 From: Jerald-Golden-B Date: Tue, 26 Aug 2025 16:49:16 +0530 Subject: [PATCH 13/34] side bar left bug fix --- app/src/components/layout/sidebarLeft/Assets.tsx | 10 +++++++--- app/src/store/builder/store.ts | 2 +- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/app/src/components/layout/sidebarLeft/Assets.tsx b/app/src/components/layout/sidebarLeft/Assets.tsx index 4e946d3..8216310 100644 --- a/app/src/components/layout/sidebarLeft/Assets.tsx +++ b/app/src/components/layout/sidebarLeft/Assets.tsx @@ -120,7 +120,12 @@ const Assets: React.FC = () => { echo.error("failed to fetch assets"); setisLoading(false); } + + if (asset === "Decals") { + fetchCategoryDecals("Safety"); + } }; + const fetchCategoryDecals = async (asset: any) => { setisLoading(true); // setSelectedCategory(asset); @@ -226,9 +231,8 @@ const Assets: React.FC = () => { {activeSubcategories.map((cat, index) => (
{ fetchCategoryDecals(cat.name); setSelectedSubCategory(cat.name); diff --git a/app/src/store/builder/store.ts b/app/src/store/builder/store.ts index 893bd65..2f72b98 100644 --- a/app/src/store/builder/store.ts +++ b/app/src/store/builder/store.ts @@ -664,6 +664,6 @@ interface DecalStore { // Create the Zustand store with types export const useDecalStore = create((set) => ({ - selectedSubCategory: null, + selectedSubCategory: 'Safety', setSelectedSubCategory: (subCategory: string | null) => set({ selectedSubCategory: subCategory }), })); From d4f12d230fb8573acb953962d805205c9c71a516 Mon Sep 17 00:00:00 2001 From: Jerald-Golden-B Date: Tue, 26 Aug 2025 17:25:04 +0530 Subject: [PATCH 14/34] added IMage store in idex db --- .../components/layout/sidebarLeft/Assets.tsx | 16 ++--- app/src/modules/builder/Decal/decal.tsx | 14 ++++ .../Decal/decalCreator/decalCreator.tsx | 44 ++++++++++++ .../{ => decalInstance}/decalInstance.tsx | 18 ++--- app/src/modules/builder/asset/assetsGroup.tsx | 1 + app/src/modules/builder/builder.tsx | 5 +- .../builder/wall/Instances/instance/wall.tsx | 2 +- app/src/store/builder/store.ts | 15 ++++ app/src/utils/indexDB/idbUtils.ts | 70 +++++++++++++++---- 9 files changed, 152 insertions(+), 33 deletions(-) create mode 100644 app/src/modules/builder/Decal/decal.tsx create mode 100644 app/src/modules/builder/Decal/decalCreator/decalCreator.tsx rename app/src/modules/builder/Decal/{ => decalInstance}/decalInstance.tsx (93%) diff --git a/app/src/components/layout/sidebarLeft/Assets.tsx b/app/src/components/layout/sidebarLeft/Assets.tsx index 8216310..824062f 100644 --- a/app/src/components/layout/sidebarLeft/Assets.tsx +++ b/app/src/components/layout/sidebarLeft/Assets.tsx @@ -2,7 +2,7 @@ import React, { useEffect, useState } from "react"; import Search from "../../ui/inputs/Search"; import { getCategoryAsset } from "../../../services/factoryBuilder/asset/assets/getCategoryAsset"; import { fetchAssets } from "../../../services/marketplace/fetchAssets"; -import { useDecalStore, useSelectedItem } from "../../../store/builder/store"; +import { useDecalStore, useDroppedDecal, useSelectedItem } from "../../../store/builder/store"; // images ------------------- import vehicle from "../../../assets/image/categories/vehicles.png"; @@ -21,7 +21,6 @@ import { HangTagIcon, NavigationIcon, } from "../../icons/ExportCommonIcons"; -import { assert } from "console"; import { getCategoryDecals } from "../../../services/factoryBuilder/asset/decals/getCategoryDecals"; // ------------------------------------- @@ -46,6 +45,7 @@ interface CategoryListProp { } const Assets: React.FC = () => { const { setSelectedItem } = useSelectedItem(); + const { setDroppedDecal } = useDroppedDecal(); const [searchValue, setSearchValue] = useState(""); const [selectedCategory, setSelectedCategory] = useState(null); const [categoryAssets, setCategoryAssets] = useState([]); @@ -304,15 +304,11 @@ const Assets: React.FC = () => { alt={asset.decalName} className="asset-image" onPointerDown={() => { - setSelectedItem({ - name: asset.decalName, - id: asset.id, - type: - asset.type === "undefined" - ? undefined - : asset.type, + setDroppedDecal({ category: asset.category, - // subType: asset.subType, + decalName: asset.decalName, + decalImage: asset.decalImage, + decalId: asset.id }); }} /> diff --git a/app/src/modules/builder/Decal/decal.tsx b/app/src/modules/builder/Decal/decal.tsx new file mode 100644 index 0000000..d14e150 --- /dev/null +++ b/app/src/modules/builder/Decal/decal.tsx @@ -0,0 +1,14 @@ +import DecalCreator from './decalCreator/decalCreator' + +function Decal() { + + return ( + <> + + + + + ) +} + +export default Decal \ No newline at end of file diff --git a/app/src/modules/builder/Decal/decalCreator/decalCreator.tsx b/app/src/modules/builder/Decal/decalCreator/decalCreator.tsx new file mode 100644 index 0000000..4335afc --- /dev/null +++ b/app/src/modules/builder/Decal/decalCreator/decalCreator.tsx @@ -0,0 +1,44 @@ +import { useEffect } from 'react'; +import { useThree } from '@react-three/fiber'; +import { useDroppedDecal } from '../../../../store/builder/store'; +import useModuleStore from '../../../../store/useModuleStore'; + +function DecalCreator() { + const { droppedDecal } = useDroppedDecal(); + const { activeModule } = useModuleStore(); + const { controls, gl, pointer, camera, raycaster, scene } = useThree(); + + useEffect(() => { + const canvasElement = gl.domElement; + + const onDrop = (event: DragEvent) => { + console.log('event: ', event); + console.log('droppedDecal: ', droppedDecal); + if (!event.dataTransfer?.files[0]) return; + + if (droppedDecal) { + } + }; + + const onDragOver = (event: any) => { + event.preventDefault(); + }; + + if (activeModule === "builder") { + canvasElement.addEventListener("drop", onDrop); + canvasElement.addEventListener("dragover", onDragOver); + } + + return () => { + canvasElement.removeEventListener("drop", onDrop); + canvasElement.removeEventListener("dragover", onDragOver); + }; + }, [droppedDecal, camera, activeModule, controls]); + + return ( + <> + + ) +} + +export default DecalCreator \ No newline at end of file diff --git a/app/src/modules/builder/Decal/decalInstance.tsx b/app/src/modules/builder/Decal/decalInstance/decalInstance.tsx similarity index 93% rename from app/src/modules/builder/Decal/decalInstance.tsx rename to app/src/modules/builder/Decal/decalInstance/decalInstance.tsx index d414419..bbb1268 100644 --- a/app/src/modules/builder/Decal/decalInstance.tsx +++ b/app/src/modules/builder/Decal/decalInstance/decalInstance.tsx @@ -1,19 +1,19 @@ import * as THREE from 'three'; import { CameraControls, Decal } from '@react-three/drei' import { useLoader, useThree } from '@react-three/fiber'; -import { useSocketStore, useToggleView, useToolMode } from '../../../store/builder/store'; -import { useBuilderStore } from '../../../store/builder/useBuilderStore'; +import { useSocketStore, useToggleView, useToolMode } from '../../../../store/builder/store'; +import { useBuilderStore } from '../../../../store/builder/useBuilderStore'; -import defaultMaterial from '../../../assets/textures/floor/wall-tex.png'; -import useModuleStore from '../../../store/useModuleStore'; -import { useSceneContext } from '../../scene/sceneContext'; +import defaultMaterial from '../../../../assets/textures/floor/wall-tex.png'; +import useModuleStore from '../../../../store/useModuleStore'; +import { useSceneContext } from '../../../scene/sceneContext'; import { useEffect, useRef } from 'react'; -import { getUserData } from '../../../functions/getUserData'; -import { useVersionContext } from '../version/versionContext'; +import { getUserData } from '../../../../functions/getUserData'; +import { useVersionContext } from '../../version/versionContext'; import { useParams } from 'react-router-dom'; -// import { upsertWallApi } from '../../../services/factoryBuilder/wall/upsertWallApi'; -// import { upsertFloorApi } from '../../../services/factoryBuilder/floor/upsertFloorApi'; +// import { upsertWallApi } from '../../../../services/factoryBuilder/wall/upsertWallApi'; +// import { upsertFloorApi } from '../../../../services/factoryBuilder/floor/upsertFloorApi'; function DecalInstance({ parent, visible = true, decal, zPosition = decal.decalPosition[2] }: { parent: Wall | Floor; visible?: boolean, decal: Decal, zPosition?: number }) { const { setSelectedWall, setSelectedFloor, selectedDecal, deletableDecal, setSelectedDecal, setDeletableDecal } = useBuilderStore(); diff --git a/app/src/modules/builder/asset/assetsGroup.tsx b/app/src/modules/builder/asset/assetsGroup.tsx index 9d36efc..0205a4c 100644 --- a/app/src/modules/builder/asset/assetsGroup.tsx +++ b/app/src/modules/builder/asset/assetsGroup.tsx @@ -363,6 +363,7 @@ function AssetsGroup({ plane }: { readonly plane: RefMesh }) { setLeft(relativeX); } }; + const onMouseUp = (evt: any) => { setIsRenameMode(false); } diff --git a/app/src/modules/builder/builder.tsx b/app/src/modules/builder/builder.tsx index ef310fc..d7b8d34 100644 --- a/app/src/modules/builder/builder.tsx +++ b/app/src/modules/builder/builder.tsx @@ -33,13 +33,14 @@ import AssetsGroup from "./asset/assetsGroup"; import DxfFile from "./dfx/LoadBlueprint"; import AislesGroup from "./aisle/aislesGroup"; import WallGroup from "./wall/wallGroup"; +import WallAssetGroup from "./wallAsset/wallAssetGroup"; import FloorGroup from "./floor/floorGroup"; import ZoneGroup from "./zone/zoneGroup"; +import Decal from "./Decal/decal"; import { useParams } from "react-router-dom"; import { useBuilderStore } from "../../store/builder/useBuilderStore"; import { getUserData } from "../../functions/getUserData"; -import WallAssetGroup from "./wallAsset/wallAssetGroup"; export default function Builder() { const state = useThree(); @@ -106,6 +107,8 @@ export default function Builder() { + + diff --git a/app/src/modules/builder/wall/Instances/instance/wall.tsx b/app/src/modules/builder/wall/Instances/instance/wall.tsx index 50d3536..f52fa4d 100644 --- a/app/src/modules/builder/wall/Instances/instance/wall.tsx +++ b/app/src/modules/builder/wall/Instances/instance/wall.tsx @@ -10,7 +10,7 @@ import { useToggleView, useWallVisibility } from '../../../../../store/builder/s import { useBuilderStore } from '../../../../../store/builder/useBuilderStore'; import * as Constants from '../../../../../types/world/worldConstants'; -import DecalInstance from '../../../Decal/decalInstance'; +import DecalInstance from '../../../Decal/decalInstance/decalInstance'; import defaultMaterial from '../../../../../assets/textures/floor/wall-tex.png'; import material1 from '../../../../../assets/textures/floor/factory wall texture.jpg'; diff --git a/app/src/store/builder/store.ts b/app/src/store/builder/store.ts index 2f72b98..c022bb7 100644 --- a/app/src/store/builder/store.ts +++ b/app/src/store/builder/store.ts @@ -163,6 +163,21 @@ export const useSelectedItem = create((set: any) => ({ setSelectedItem: (x: any) => set(() => ({ selectedItem: x })), })); +type DroppedDecalType = { + category: string; + decalName: string; + decalImage: string; + decalId: string; +}; + +export const useDroppedDecal = create<{ + droppedDecal: DroppedDecalType | null; + setDroppedDecal: (x: DroppedDecalType | null) => void; +}>((set) => ({ + droppedDecal: null, + setDroppedDecal: (x) => set({ droppedDecal: x }), +})); + export const useNavMesh = create((set: any) => ({ navMesh: null, setNavMesh: (x: any) => set({ navMesh: x }), diff --git a/app/src/utils/indexDB/idbUtils.ts b/app/src/utils/indexDB/idbUtils.ts index 24b1448..79910bb 100644 --- a/app/src/utils/indexDB/idbUtils.ts +++ b/app/src/utils/indexDB/idbUtils.ts @@ -1,15 +1,19 @@ -const DB_NAME = 'GLTFStorage'; -const STORE_NAME = 'models'; const DB_VERSION = 1; -export function initializeDB(): Promise { +const ASSET_DB_NAME = 'GLTFStorage'; +const ASSET_STORE_NAME = 'models'; + +const IMAGE_DB_NAME = 'ImageStorage'; +const IMAGE_STORE_NAME = 'images'; + +export function initializeAssetDB(): Promise { return new Promise((resolve, reject) => { - const request = indexedDB.open(DB_NAME, DB_VERSION); + const request = indexedDB.open(ASSET_DB_NAME, DB_VERSION); request.onupgradeneeded = () => { const db = request.result; - if (!db.objectStoreNames.contains(STORE_NAME)) { - db.createObjectStore(STORE_NAME); + if (!db.objectStoreNames.contains(ASSET_STORE_NAME)) { + db.createObjectStore(ASSET_STORE_NAME); } }; @@ -19,11 +23,11 @@ export function initializeDB(): Promise { } export async function storeGLTF(key: string, file: Blob): Promise { - const db = await initializeDB(); + const db = await initializeAssetDB(); return new Promise((resolve, reject) => { - const transaction = db.transaction(STORE_NAME, 'readwrite'); - const store = transaction.objectStore(STORE_NAME); + const transaction = db.transaction(ASSET_STORE_NAME, 'readwrite'); + const store = transaction.objectStore(ASSET_STORE_NAME); const request = store.put(file, key); request.onsuccess = () => resolve(); @@ -32,11 +36,53 @@ export async function storeGLTF(key: string, file: Blob): Promise { } export async function retrieveGLTF(key: string): Promise { - const db = await initializeDB(); + const db = await initializeAssetDB(); return new Promise((resolve, reject) => { - const transaction = db.transaction(STORE_NAME, 'readonly'); - const store = transaction.objectStore(STORE_NAME); + const transaction = db.transaction(ASSET_STORE_NAME, 'readonly'); + const store = transaction.objectStore(ASSET_STORE_NAME); + const request = store.get(key); + + request.onsuccess = () => resolve(request.result as Blob | undefined); + request.onerror = () => reject(request.error); + }); +} + +export function initializeImageDB(): Promise { + return new Promise((resolve, reject) => { + const request = indexedDB.open(IMAGE_DB_NAME, DB_VERSION); + + request.onupgradeneeded = () => { + const db = request.result; + if (!db.objectStoreNames.contains(IMAGE_STORE_NAME)) { + db.createObjectStore(IMAGE_STORE_NAME); + } + }; + + request.onsuccess = () => resolve(request.result); + request.onerror = () => reject(request.error); + }); +} + +export async function storeImage(key: string, file: Blob): Promise { + const db = await initializeImageDB(); + + return new Promise((resolve, reject) => { + const transaction = db.transaction(IMAGE_STORE_NAME, 'readwrite'); + const store = transaction.objectStore(IMAGE_STORE_NAME); + const request = store.put(file, key); + + request.onsuccess = () => resolve(); + request.onerror = () => reject(request.error); + }); +} + +export async function retrieveImage(key: string): Promise { + const db = await initializeImageDB(); + + return new Promise((resolve, reject) => { + const transaction = db.transaction(IMAGE_STORE_NAME, 'readonly'); + const store = transaction.objectStore(IMAGE_STORE_NAME); const request = store.get(key); request.onsuccess = () => resolve(request.result as Blob | undefined); From d129a8688574b2685a933a8214c7ec4adff19692 Mon Sep 17 00:00:00 2001 From: Jerald-Golden-B Date: Tue, 26 Aug 2025 17:39:45 +0530 Subject: [PATCH 15/34] added cache and indexdb loading for decal --- .../Decal/decalInstance/decalInstance.tsx | 107 +++++++++++++++++- .../builder/wall/Instances/instance/wall.tsx | 2 +- 2 files changed, 103 insertions(+), 6 deletions(-) diff --git a/app/src/modules/builder/Decal/decalInstance/decalInstance.tsx b/app/src/modules/builder/Decal/decalInstance/decalInstance.tsx index bbb1268..6be22ee 100644 --- a/app/src/modules/builder/Decal/decalInstance/decalInstance.tsx +++ b/app/src/modules/builder/Decal/decalInstance/decalInstance.tsx @@ -1,13 +1,14 @@ import * as THREE from 'three'; import { CameraControls, Decal } from '@react-three/drei' -import { useLoader, useThree } from '@react-three/fiber'; +import { useThree } from '@react-three/fiber'; import { useSocketStore, useToggleView, useToolMode } from '../../../../store/builder/store'; import { useBuilderStore } from '../../../../store/builder/useBuilderStore'; +import { retrieveImage, storeImage } from '../../../../utils/indexDB/idbUtils'; import defaultMaterial from '../../../../assets/textures/floor/wall-tex.png'; import useModuleStore from '../../../../store/useModuleStore'; import { useSceneContext } from '../../../scene/sceneContext'; -import { useEffect, useRef } from 'react'; +import { useEffect, useRef, useState } from 'react'; import { getUserData } from '../../../../functions/getUserData'; import { useVersionContext } from '../../version/versionContext'; import { useParams } from 'react-router-dom'; @@ -28,12 +29,108 @@ function DecalInstance({ parent, visible = true, decal, zPosition = decal.decalP const { selectedVersion } = selectedVersionStore(); const { projectId } = useParams(); const { socket } = useSocketStore(); - const material = useLoader(THREE.TextureLoader, defaultMaterial); - const { raycaster, pointer, camera, scene, gl, controls } = useThree(); const isDraggingRef = useRef(false); const dragOffsetRef = useRef(null); + const [texture, setTexture] = useState(null); + const [isLoading, setIsLoading] = useState(true); + + const loadDefaultTexture = () => { + const textureLoader = new THREE.TextureLoader(); + textureLoader.load( + defaultMaterial, + (fallbackTex) => { + fallbackTex.name = "default-decal"; + setTexture(fallbackTex); + setIsLoading(false); + }, + undefined, + (error) => { + console.error("Error loading default decal texture:", error); + setIsLoading(false); + } + ); + }; + + const loadDecalTexture = async (decalId: string) => { + setIsLoading(true); + + try { + const cachedTexture = THREE.Cache.get(decalId); + if (cachedTexture) { + setTexture(cachedTexture); + setIsLoading(false); + return; + } + + const indexedDBTexture = await retrieveImage(decalId); + if (indexedDBTexture) { + const blobUrl = URL.createObjectURL(indexedDBTexture); + const textureLoader = new THREE.TextureLoader(); + textureLoader.load( + blobUrl, + (tex) => { + URL.revokeObjectURL(blobUrl); + tex.name = decalId; + THREE.Cache.add(decalId, tex); + setTexture(tex); + setIsLoading(false); + }, + undefined, + (error) => { + console.error(`Error loading texture from IndexedDB:`, error); + URL.revokeObjectURL(blobUrl); + loadFromBackend(decalId); + } + ); + return; + } + + loadFromBackend(decalId); + } catch (error) { + console.error("Error loading decal texture:", error); + loadDefaultTexture(); + } + }; + + const loadFromBackend = (decalId: string) => { + console.log('decalId: ', decalId); + const textureUrl = `${process.env.REACT_APP_SERVER_MARKETPLACE_URL}/api/v2/DecalFile/${decalId}`; + const textureLoader = new THREE.TextureLoader(); + + textureLoader.load( + textureUrl, + async (tex) => { + tex.name = decalId; + THREE.Cache.add(decalId, tex); + setTexture(tex); + setIsLoading(false); + + try { + const response = await fetch(textureUrl); + const blob = await response.blob(); + await storeImage(decalId, blob); + } catch (error) { + console.error("Error storing texture in IndexedDB:", error); + } + }, + undefined, + (error) => { + console.error(`Error loading texture from backend:`, error); + loadDefaultTexture(); + } + ); + }; + + useEffect(() => { + if (decal.decalId) { + loadDecalTexture(decal.decalId); + } else { + loadDefaultTexture(); + } + }, [decal.decalId]); + useEffect(() => { if (!toggleView && activeModule === 'builder') { if (toolMode !== 'cursor') { @@ -227,7 +324,7 @@ function DecalInstance({ parent, visible = true, decal, zPosition = decal.decalP }} > Date: Thu, 28 Aug 2025 15:50:45 +0530 Subject: [PATCH 16/34] added decal dropping to scene, decal movement from one wall to another wall, one floor to another floor, one wall to floor and one floor to wall --- .../Decal/decalCreator/decalCreator.tsx | 117 ++++++- .../Decal/decalInstance/decalInstance.tsx | 229 ++----------- .../eventHandler/useDecalEventHandlers.ts | 301 ++++++++++++++++++ .../builder/asset/models/model/model.tsx | 7 + .../Instances/Instance/floorInstance.tsx | 117 +++---- .../builder/wall/Instances/instance/wall.tsx | 16 +- .../scene/postProcessing/postProcessing.tsx | 2 +- app/src/store/builder/useBuilderStore.ts | 29 +- app/src/store/builder/useFloorStore.ts | 25 +- app/src/store/builder/useWallStore.ts | 21 +- 10 files changed, 568 insertions(+), 296 deletions(-) create mode 100644 app/src/modules/builder/Decal/eventHandler/useDecalEventHandlers.ts diff --git a/app/src/modules/builder/Decal/decalCreator/decalCreator.tsx b/app/src/modules/builder/Decal/decalCreator/decalCreator.tsx index 4335afc..221a2bf 100644 --- a/app/src/modules/builder/Decal/decalCreator/decalCreator.tsx +++ b/app/src/modules/builder/Decal/decalCreator/decalCreator.tsx @@ -1,22 +1,129 @@ +import { MathUtils } from 'three'; import { useEffect } from 'react'; import { useThree } from '@react-three/fiber'; -import { useDroppedDecal } from '../../../../store/builder/store'; +import { useParams } from 'react-router-dom'; +import { useDroppedDecal, useSocketStore } from '../../../../store/builder/store'; import useModuleStore from '../../../../store/useModuleStore'; +import { useSceneContext } from '../../../scene/sceneContext'; +import { useVersionContext } from '../../version/versionContext'; + +import { getUserData } from '../../../../functions/getUserData'; + +// import { upsertWallApi } from '../../../../services/factoryBuilder/wall/upsertWallApi'; +// import { upsertFloorApi } from '../../../../services/factoryBuilder/floor/upsertFloorApi'; function DecalCreator() { + const { wallStore, floorStore } = useSceneContext(); + const { addDecal: addDecalOnWall, getWallById } = wallStore(); + const { addDecal : addDecalOnFloor, getFloorById } = floorStore(); const { droppedDecal } = useDroppedDecal(); const { activeModule } = useModuleStore(); + const { userId, organization } = getUserData(); + const { selectedVersionStore } = useVersionContext(); + const { selectedVersion } = selectedVersionStore(); + const { projectId } = useParams(); + const { socket } = useSocketStore(); const { controls, gl, pointer, camera, raycaster, scene } = useThree(); useEffect(() => { const canvasElement = gl.domElement; const onDrop = (event: DragEvent) => { - console.log('event: ', event); - console.log('droppedDecal: ', droppedDecal); - if (!event.dataTransfer?.files[0]) return; - if (droppedDecal) { + pointer.x = (event.clientX / window.innerWidth) * 2 - 1; + pointer.y = -(event.clientY / window.innerHeight) * 2 + 1; + raycaster.setFromCamera(pointer, camera); + const intersects = raycaster.intersectObjects(scene.children, true); + const wallIntersect = intersects.find(i => i.object.userData && i.object.userData.wallUuid); + const floorIntersect = intersects.find(i => i.object.userData && i.object.userData.floorUuid); + + if (wallIntersect) { + const wall = getWallById(wallIntersect.object.userData.wallUuid); + if (!wall) return; + + const point = wallIntersect.object.worldToLocal(wallIntersect.point.clone()); + + const decal: Decal = { + decalUuid: MathUtils.generateUUID(), + decalName: droppedDecal.decalName, + decalId: droppedDecal.decalId, + decalType: { + type: 'Wall', + wallUuid: wallIntersect.object.userData.wallUuid, + }, + decalPosition: [point.x, point.y, wall.wallThickness / 2 + 0.001], + decalRotation: 0, + decalOpacity: 1, + decalScale: 1, + } + + addDecalOnWall(wallIntersect.object.userData.wallUuid, decal); + + setTimeout(() => { + const updatedWall = getWallById(wallIntersect.object.userData.wallUuid); + if (updatedWall) { + if (projectId && updatedWall) { + // API + + // upsertWallApi(projectId, selectedVersion?.versionId || '', updatedWall); + + // SOCKET + + const data = { + wallData: updatedWall, + projectId: projectId, + versionId: selectedVersion?.versionId || '', + userId: userId, + organization: organization + } + + socket.emit('v1:model-Wall:add', data); + } + } + }, 0) + } else if (floorIntersect) { + const floor = getFloorById(floorIntersect.object.userData.floorUuid); + if (!floor) return; + + const point = floorIntersect.object.worldToLocal(floorIntersect.point.clone()); + + const decal: Decal = { + decalUuid: MathUtils.generateUUID(), + decalName: droppedDecal.decalName, + decalId: droppedDecal.decalId, + decalType: { + type: 'Floor', + floorUuid: floorIntersect.object.userData.floorUuid, + }, + decalPosition: [point.x, point.y, -0.001], + decalRotation: 0, + decalOpacity: 1, + decalScale: 1, + } + + addDecalOnFloor(floorIntersect.object.userData.floorUuid, decal); + + setTimeout(() => { + const updatedFloor = getFloorById(floorIntersect.object.userData.floorUuid); + if (projectId && updatedFloor) { + // API + + // upsertFloorApi(projectId, selectedVersion?.versionId || '', updatedFloor); + + // SOCKET + + const data = { + floorData: updatedFloor, + projectId: projectId, + versionId: selectedVersion?.versionId || '', + userId: userId, + organization: organization + } + + socket.emit('v1:model-Floor:add', data); + } + }, 0) + } } }; diff --git a/app/src/modules/builder/Decal/decalInstance/decalInstance.tsx b/app/src/modules/builder/Decal/decalInstance/decalInstance.tsx index 6be22ee..8d0540a 100644 --- a/app/src/modules/builder/Decal/decalInstance/decalInstance.tsx +++ b/app/src/modules/builder/Decal/decalInstance/decalInstance.tsx @@ -1,40 +1,38 @@ import * as THREE from 'three'; -import { CameraControls, Decal } from '@react-three/drei' -import { useThree } from '@react-three/fiber'; -import { useSocketStore, useToggleView, useToolMode } from '../../../../store/builder/store'; +import { Decal } from '@react-three/drei' +import { useToggleView, useToolMode } from '../../../../store/builder/store'; import { useBuilderStore } from '../../../../store/builder/useBuilderStore'; import { retrieveImage, storeImage } from '../../../../utils/indexDB/idbUtils'; import defaultMaterial from '../../../../assets/textures/floor/wall-tex.png'; import useModuleStore from '../../../../store/useModuleStore'; -import { useSceneContext } from '../../../scene/sceneContext'; import { useEffect, useRef, useState } from 'react'; -import { getUserData } from '../../../../functions/getUserData'; -import { useVersionContext } from '../../version/versionContext'; -import { useParams } from 'react-router-dom'; +import { useDecalEventHandlers } from '../eventHandler/useDecalEventHandlers'; // import { upsertWallApi } from '../../../../services/factoryBuilder/wall/upsertWallApi'; // import { upsertFloorApi } from '../../../../services/factoryBuilder/floor/upsertFloorApi'; function DecalInstance({ parent, visible = true, decal, zPosition = decal.decalPosition[2] }: { parent: Wall | Floor; visible?: boolean, decal: Decal, zPosition?: number }) { - const { setSelectedWall, setSelectedFloor, selectedDecal, deletableDecal, setSelectedDecal, setDeletableDecal } = useBuilderStore(); - const { wallStore, floorStore } = useSceneContext(); - const { removeDecal: removeDecalInWall, updateDecalPosition: updateDecalPositionInWall, getWallById } = wallStore(); - const { removeDecal: removeDecalInFloor } = floorStore(); + const url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`; + const { selectedDecal, deletableDecal, setSelectedDecal, setDeletableDecal } = useBuilderStore(); const { toolMode } = useToolMode(); const { toggleView } = useToggleView(); const { activeModule } = useModuleStore(); - const { userId, organization } = getUserData(); - const { selectedVersionStore } = useVersionContext(); - const { selectedVersion } = selectedVersionStore(); - const { projectId } = useParams(); - const { socket } = useSocketStore(); - const { raycaster, pointer, camera, scene, gl, controls } = useThree(); - const isDraggingRef = useRef(false); - const dragOffsetRef = useRef(null); + const decalRef = useRef(null); + + useEffect(() => { + if (selectedDecal?.decalData.decalUuid === decal.decalUuid && !selectedDecal.decalMesh) { + setSelectedDecal({ decalData: selectedDecal.decalData, decalMesh: decalRef.current }); + } + }, [selectedDecal]) + + const { handlePointerMissed, handlePointerLeave, handleClick, handlePointerDown, handlePointerEnter } = useDecalEventHandlers({ parent, decal, visible }); const [texture, setTexture] = useState(null); - const [isLoading, setIsLoading] = useState(true); + + const logDecalStatus = (decalId: string, status: string) => { + // console.log(decalId, status); + } const loadDefaultTexture = () => { const textureLoader = new THREE.TextureLoader(); @@ -43,24 +41,22 @@ function DecalInstance({ parent, visible = true, decal, zPosition = decal.decalP (fallbackTex) => { fallbackTex.name = "default-decal"; setTexture(fallbackTex); - setIsLoading(false); + logDecalStatus(decal.decalId, 'default-loaded'); }, undefined, (error) => { console.error("Error loading default decal texture:", error); - setIsLoading(false); } ); }; const loadDecalTexture = async (decalId: string) => { - setIsLoading(true); try { const cachedTexture = THREE.Cache.get(decalId); if (cachedTexture) { setTexture(cachedTexture); - setIsLoading(false); + logDecalStatus(decalId, 'cache-loaded'); return; } @@ -75,7 +71,7 @@ function DecalInstance({ parent, visible = true, decal, zPosition = decal.decalP tex.name = decalId; THREE.Cache.add(decalId, tex); setTexture(tex); - setIsLoading(false); + logDecalStatus(decalId, 'indexedDB-loaded'); }, undefined, (error) => { @@ -95,8 +91,8 @@ function DecalInstance({ parent, visible = true, decal, zPosition = decal.decalP }; const loadFromBackend = (decalId: string) => { - console.log('decalId: ', decalId); - const textureUrl = `${process.env.REACT_APP_SERVER_MARKETPLACE_URL}/api/v2/DecalFile/${decalId}`; + + const textureUrl = `${url_Backend_dwinzo}/api/v1/DecalImage/${decalId}`; const textureLoader = new THREE.TextureLoader(); textureLoader.load( @@ -105,7 +101,7 @@ function DecalInstance({ parent, visible = true, decal, zPosition = decal.decalP tex.name = decalId; THREE.Cache.add(decalId, tex); setTexture(tex); - setIsLoading(false); + logDecalStatus(decalId, 'backend-loaded'); try { const response = await fetch(textureUrl); @@ -145,183 +141,20 @@ function DecalInstance({ parent, visible = true, decal, zPosition = decal.decalP } }, [toggleView, toolMode, activeModule, selectedDecal, deletableDecal]); - useEffect(() => { - const canvasElement = gl.domElement; - - const handlePointerMove = (e: PointerEvent) => { - if (!isDraggingRef.current || !selectedDecal || selectedDecal.decalData.decalUuid !== decal.decalUuid) return; - - raycaster.setFromCamera(pointer, camera); - const intersects = raycaster.intersectObjects(scene.children, true); - - const wallIntersect = intersects.find(i => i.object.userData && 'wallUuid' in parent && i.object.userData.wallUuid === parent.wallUuid); - - if (wallIntersect) { - const point = wallIntersect.object.worldToLocal(wallIntersect.point.clone()); - - let offset = dragOffsetRef.current || new THREE.Vector3(0, 0, 0); - - updateDecalPositionInWall(decal.decalUuid, [point.x + offset.x, point.y + offset.y, decal.decalPosition[2]]); - } - }; - - const handlePointerUp = (e: PointerEvent) => { - if (controls) { - (controls as CameraControls).enabled = true; - } - if (isDraggingRef.current) { - isDraggingRef.current = false; - dragOffsetRef.current = null; - - if ('wallUuid' in parent) { - setTimeout(() => { - const updatedWall = getWallById(parent.wallUuid); - - if (updatedWall) { - if (projectId && updatedWall) { - // API - - // upsertWallApi(projectId, selectedVersion?.versionId || '', updatedWall); - - // SOCKET - - const data = { - wallData: updatedWall, - projectId: projectId, - versionId: selectedVersion?.versionId || '', - userId: userId, - organization: organization - } - - socket.emit('v1:model-Wall:add', data); - } - } - }, 0) - } - - } - }; - - if (activeModule === 'builder' && !toggleView) { - canvasElement.addEventListener('pointermove', handlePointerMove); - canvasElement.addEventListener('pointerup', handlePointerUp); - } - - return () => { - canvasElement.removeEventListener('pointermove', handlePointerMove); - canvasElement.removeEventListener('pointerup', handlePointerUp); - }; - }, [gl, camera, scene, raycaster, selectedDecal, decal, parent, activeModule, toggleView, projectId, selectedVersion, userId, organization, socket]); - - const deleteDecal = (decalUuid: string, parent: Wall | Floor) => { - if ('wallUuid' in parent) { - const updatedWall = removeDecalInWall(decalUuid); - - if (projectId && updatedWall) { - // API - - // upsertWallApi(projectId, selectedVersion?.versionId || '', updatedWall); - - // SOCKET - - const data = { - wallData: updatedWall, - projectId: projectId, - versionId: selectedVersion?.versionId || '', - userId: userId, - organization: organization - } - - socket.emit('v1:model-Wall:add', data); - } - } else if ('floorUuid' in parent) { - const updatedFloor = removeDecalInFloor(decalUuid); - - if (projectId && updatedFloor) { - // API - - // upsertFloorApi(projectId, selectedVersion?.versionId || '', updatedFloor); - - // SOCKET - - const data = { - floorData: updatedFloor, - projectId: projectId, - versionId: selectedVersion?.versionId || '', - userId: userId, - organization: organization - } - - socket.emit('v1:model-Floor:add', data); - } - } - } - return ( { - if (visible && !toggleView && activeModule === 'builder') { - if (e.object.userData.decalUuid && toolMode === 'cursor') { - e.stopPropagation(); - isDraggingRef.current = true; - if (controls) { - (controls as CameraControls).enabled = false; - } - setSelectedDecal({ decalMesh: e.object, decalData: decal }); - setSelectedWall(null); - setSelectedFloor(null); - - const localIntersect = e.object.worldToLocal(e.point.clone()); - dragOffsetRef.current = new THREE.Vector3(decal.decalPosition[0] - localIntersect.x, decal.decalPosition[1] - localIntersect.y, 0); - } - } - }} - - onClick={(e) => { - if (visible && !toggleView && activeModule === 'builder') { - if (e.object.userData.decalUuid) { - e.stopPropagation(); - if (toolMode === 'cursor') { - setSelectedDecal({ decalMesh: e.object, decalData: decal }); - setSelectedWall(null); - setSelectedFloor(null); - } else if (toolMode === '3D-Delete') { - deleteDecal(e.object.userData.decalUuid, parent); - } - } - } - }} - onPointerEnter={(e) => { - if (visible && !toggleView && activeModule === 'builder') { - if (e.object.userData.decalUuid) { - e.stopPropagation(); - if (toolMode === '3D-Delete') { - setDeletableDecal(e.object); - } - } - } - }} - onPointerLeave={(e) => { - if (visible && !toggleView && activeModule === 'builder') { - if (e.object.userData.decalUuid) { - e.stopPropagation(); - if (toolMode === '3D-Delete' && deletableDecal && deletableDecal?.userData.decalUuid === e.object.userData.decalUuid) { - setDeletableDecal(null); - } - } - } - }} - onPointerMissed={() => { - if (selectedDecal && selectedDecal.decalMesh.userData.decalUuid === decal.decalUuid) { - setSelectedDecal(null); - } - }} + onPointerDown={(e) => { if (e.button === 0) handlePointerDown(e) }} + onClick={(e) => { handleClick(e) }} + onPointerEnter={(e) => { handlePointerEnter(e) }} + onPointerLeave={(e) => { handlePointerLeave(e) }} + onPointerMissed={() => handlePointerMissed()} > { + if (activeModule !== 'builder' || toggleView || !decalDragState.isDragging || !selectedDecal || selectedDecal.decalData.decalUuid !== decal.decalUuid) return; + + raycaster.setFromCamera(pointer, camera); + const intersects = raycaster.intersectObjects(scene.children, true); + + const wallIntersect = intersects.find(i => i.object.userData?.wallUuid); + const floorIntersect = intersects.find(i => i.object.userData?.floorUuid); + + let offset = decalDragState.dragOffset || new THREE.Vector3(0, 0, 0); + + if (wallIntersect) { + const wallUuid = wallIntersect.object.userData.wallUuid; + const point = wallIntersect.object.worldToLocal(wallIntersect.point.clone()); + + if ("wallUuid" in parent && parent.wallUuid === wallUuid && decal.decalType.type === 'Wall') { + updateDecalPositionInWall(decal.decalUuid, [point.x + offset.x, point.y + offset.y, decal.decalPosition[2]]); + } else if (decal.decalType.type === 'Wall' && wallUuid) { + deleteDecal(decal.decalUuid, parent); + + const addedDecal = addDecalToWall(wallUuid, { + ...decal, + decalPosition: [point.x + offset.x, point.y + offset.y, decal.decalPosition[2]], + decalType: { type: 'Wall', wallUuid: wallUuid } + }); + + if (addedDecal) { + setSelectedDecal({ decalMesh: null, decalData: addedDecal }) + } + } else if (decal.decalType.type === 'Floor' && wallUuid) { + deleteDecal(decal.decalUuid, parent); + const wall = getWallById(wallUuid); + if (!wall) return; + + const addedDecal = addDecalToWall(wallUuid, { + ...decal, + decalPosition: [point.x + offset.x, point.y + offset.y, wall.wallThickness / 2 + 0.001], + decalType: { type: 'Wall', wallUuid: wallUuid } + }); + + if (addedDecal) { + setSelectedDecal({ decalMesh: null, decalData: addedDecal }) + } + } + } else if (floorIntersect) { + const floorUuid = floorIntersect.object.userData.floorUuid; + const point = floorIntersect.object.worldToLocal(floorIntersect.point.clone()); + + if ("floorUuid" in parent && parent.floorUuid === floorUuid && decal.decalType.type === 'Floor') { + updateDecalPositionInFloor(decal.decalUuid, [point.x + offset.x, point.y + offset.y, decal.decalPosition[2]]); + } else if (decal.decalType.type === 'Floor' && floorUuid) { + deleteDecal(decal.decalUuid, parent); + + const addedDecal = addDecalToFloor(floorUuid, { + ...decal, + decalPosition: [point.x + offset.x, point.y + offset.y, decal.decalPosition[2]], + decalType: { type: 'Floor', floorUuid: floorUuid } + }); + + if (addedDecal) { + setSelectedDecal({ decalMesh: null, decalData: addedDecal }) + } + } else if (decal.decalType.type === 'Wall' && floorUuid) { + deleteDecal(decal.decalUuid, parent); + const floor = getFloorById(floorUuid); + if (!floor) return; + + const addedDecal = addDecalToFloor(floorUuid, { + ...decal, + decalPosition: [point.x + offset.x, point.y + offset.y, -0.001], + decalType: { type: 'Floor', floorUuid: floorUuid } + }); + + if (addedDecal) { + setSelectedDecal({ decalMesh: null, decalData: addedDecal }) + } + } + } + }); + + const handlePointerUp = (e: PointerEvent) => { + if (controls) { + (controls as CameraControls).enabled = true; + } + if (decalDragState.isDragging) { + setDecalDragState(false, null, null); + + if ('wallUuid' in parent) { + setTimeout(() => { + const updatedWall = getWallById(parent.wallUuid); + if (updatedWall) { + if (projectId && updatedWall) { + // API + + // upsertWallApi(projectId, selectedVersion?.versionId || '', updatedWall); + + // SOCKET + + const data = { + wallData: updatedWall, + projectId: projectId, + versionId: selectedVersion?.versionId || '', + userId: userId, + organization: organization + } + + socket.emit('v1:model-Wall:add', data); + } + } + }, 0) + } else if ('floorUuid' in parent) { + setTimeout(() => { + const updatedFloor = parent; + + if (projectId && updatedFloor) { + // API + + // upsertFloorApi(projectId, selectedVersion?.versionId || '', updatedFloor); + + // SOCKET + + const data = { + floorData: updatedFloor, + projectId: projectId, + versionId: selectedVersion?.versionId || '', + userId: userId, + organization: organization + } + + socket.emit('v1:model-Floor:add', data); + } + }, 0) + } + } + }; + + const deleteDecal = (decalUuid: string, parent: Wall | Floor) => { + if ('wallUuid' in parent) { + const updatedWall = removeDecalInWall(decalUuid); + + if (projectId && updatedWall) { + // API + + // upsertWallApi(projectId, selectedVersion?.versionId || '', updatedWall); + + // SOCKET + + const data = { + wallData: updatedWall, + projectId: projectId, + versionId: selectedVersion?.versionId || '', + userId: userId, + organization: organization + } + + socket.emit('v1:model-Wall:add', data); + } + } else if ('floorUuid' in parent) { + const updatedFloor = removeDecalInFloor(decalUuid); + + if (projectId && updatedFloor) { + // API + + // upsertFloorApi(projectId, selectedVersion?.versionId || '', updatedFloor); + + // SOCKET + + const data = { + floorData: updatedFloor, + projectId: projectId, + versionId: selectedVersion?.versionId || '', + userId: userId, + organization: organization + } + + socket.emit('v1:model-Floor:add', data); + } + } + } + + const handlePointerDown = (e: ThreeEvent) => { + if (visible && !toggleView && activeModule === 'builder') { + if (e.object.userData.decalUuid && toolMode === 'cursor') { + e.stopPropagation(); + setDecalDragState(true, decal.decalUuid, null); + if (controls) { + (controls as CameraControls).enabled = false; + } + setSelectedDecal({ decalMesh: e.object, decalData: decal }); + setSelectedWall(null); + setSelectedFloor(null); + + const localIntersect = e.object.worldToLocal(e.point.clone()); + let dragOffset = new THREE.Vector3(decal.decalPosition[0] - localIntersect.x, decal.decalPosition[1] - localIntersect.y, 0); + setDecalDragState(true, decal.decalUuid, dragOffset); + } + } + }; + + const handleClick = (e: ThreeEvent) => { + if (visible && !toggleView && activeModule === 'builder') { + if (e.object.userData.decalUuid) { + e.stopPropagation(); + if (toolMode === 'cursor') { + setSelectedDecal({ decalMesh: e.object, decalData: decal }); + setSelectedWall(null); + setSelectedFloor(null); + } else if (toolMode === '3D-Delete') { + deleteDecal(e.object.userData.decalUuid, parent); + } + } + } + }; + + const handlePointerEnter = (e: ThreeEvent) => { + if (visible && !toggleView && activeModule === 'builder') { + if (e.object.userData.decalUuid) { + e.stopPropagation(); + if (toolMode === '3D-Delete') { + setDeletableDecal(e.object); + } + } + } + }; + + const handlePointerLeave = (e: ThreeEvent) => { + if (visible && !toggleView && activeModule === 'builder') { + if (e.object.userData.decalUuid) { + e.stopPropagation(); + if (toolMode === '3D-Delete' && deletableDecal && deletableDecal?.userData.decalUuid === e.object.userData.decalUuid) { + setDeletableDecal(null); + } + } + } + }; + + const handlePointerMissed = () => { + if (selectedDecal && selectedDecal.decalMesh && selectedDecal.decalMesh.userData.decalUuid === decal.decalUuid) { + setSelectedDecal(null); + } + }; + + useEffect(() => { + const canvasElement = gl.domElement; + + if (activeModule === 'builder' && !toggleView && selectedDecal && selectedDecal.decalData.decalUuid === decal.decalUuid) { + canvasElement.addEventListener('pointerup', handlePointerUp); + } + + return () => { + canvasElement.removeEventListener('pointerup', handlePointerUp); + }; + }, [gl, activeModule, toggleView, selectedDecal, camera, controls, visible, parent, decal, decalDragState]); + + return { + handlePointerDown, + handleClick, + handlePointerEnter, + handlePointerLeave, + handlePointerMissed, + deleteDecal + }; +} \ No newline at end of file diff --git a/app/src/modules/builder/asset/models/model/model.tsx b/app/src/modules/builder/asset/models/model/model.tsx index 2f5f77c..ca1b406 100644 --- a/app/src/modules/builder/asset/models/model/model.tsx +++ b/app/src/modules/builder/asset/models/model/model.tsx @@ -88,6 +88,10 @@ function Model({ asset, isRendered, loader }: { readonly asset: Asset, isRendere } }, [gltfScene]); + const logModelStatus = (modelId: string, status: string) => { + // console.log(modelId, status); + } + useEffect(() => { // Calculate Bounding Box const calculateBoundingBox = (scene: THREE.Object3D) => { @@ -103,6 +107,7 @@ function Model({ asset, isRendered, loader }: { readonly asset: Asset, isRendere clone.animations = cachedModel.animations || []; setGltfScene(clone); calculateBoundingBox(clone); + logModelStatus(assetId, 'cache-loaded'); return; } @@ -118,6 +123,7 @@ function Model({ asset, isRendered, loader }: { readonly asset: Asset, isRendere THREE.Cache.add(assetId, gltf); setGltfScene(gltf.scene.clone()); calculateBoundingBox(gltf.scene); + logModelStatus(assetId, 'indexedDB-loaded'); }, undefined, (error) => { @@ -140,6 +146,7 @@ function Model({ asset, isRendered, loader }: { readonly asset: Asset, isRendere THREE.Cache.add(assetId, gltf); setGltfScene(gltf.scene.clone()); calculateBoundingBox(gltf.scene); + logModelStatus(assetId, 'backend-loaded'); }) .catch((error) => { console.error( diff --git a/app/src/modules/builder/floor/Instances/Instance/floorInstance.tsx b/app/src/modules/builder/floor/Instances/Instance/floorInstance.tsx index f89b38d..4ea5b43 100644 --- a/app/src/modules/builder/floor/Instances/Instance/floorInstance.tsx +++ b/app/src/modules/builder/floor/Instances/Instance/floorInstance.tsx @@ -1,12 +1,13 @@ import { useMemo } from "react"; -import { Shape, Vector2, DoubleSide, TextureLoader, RepeatWrapping, SRGBColorSpace, NoColorSpace, } from "three"; +import { Shape, Vector2, DoubleSide, TextureLoader, RepeatWrapping, SRGBColorSpace, NoColorSpace, ExtrudeGeometry, Vector3, Euler, } from "three"; import { useLoader } from "@react-three/fiber"; -import { Extrude } from "@react-three/drei"; import useModuleStore from "../../../../../store/useModuleStore"; import { useBuilderStore } from "../../../../../store/builder/useBuilderStore"; import { useToggleView } from "../../../../../store/builder/store"; import * as Constants from "../../../../../types/world/worldConstants"; +import DecalInstance from "../../../Decal/decalInstance/decalInstance"; + import texturePath from "../../../../../assets/textures/floor/white.png"; import texturePathDark from "../../../../../assets/textures/floor/black.png"; import material1 from "../../../../../assets/textures/floor/factory wall texture.jpg"; @@ -67,20 +68,26 @@ function FloorInstance({ floor }: { floor: Floor }) { }, }; - const shape = useMemo(() => { - const shape = new Shape(); + const shapeData = useMemo(() => { const points = floor.points.map((p) => new Vector2(p.position[0], p.position[2])); if (points.length < 3) return null; - shape.moveTo(points[0].x, points[0].y); - for (let i = 1; i < points.length; i++) { - shape.lineTo(points[i].x, points[i].y); + + const centroidX = points.reduce((sum, p) => sum + p.x, 0) / points.length; + const centroidY = points.reduce((sum, p) => sum + p.y, 0) / points.length; + + const relativePoints = points.map((p) => new Vector2(p.x - centroidX, p.y - centroidY)); + + const shape = new Shape(); + shape.moveTo(relativePoints[0].x, relativePoints[0].y); + for (let i = 1; i < relativePoints.length; i++) { + shape.lineTo(relativePoints[i].x, relativePoints[i].y); } - return shape; + + return { shape, center: [centroidX, centroidY] }; }, [floor]); const textureScale = Constants.floorConfig.textureScale; - // Helper function to handle texture maps and filter out null values function getMaterialMaps(material: any, defaultMap: any) { const materialMap = material.map || defaultMap; const normalMap = material.normalMap || null; @@ -90,26 +97,18 @@ function FloorInstance({ floor }: { floor: Floor }) { return [materialMap, normalMap, roughnessMap, metalnessMap].filter((texture): texture is string => texture !== null); } - // Default material map const defaultMaterialMap = materials["Default Material"].map; - // Get top and side material maps const topMaterial = materials[floor.topMaterial]; const sideMaterial = materials[floor.sideMaterial]; - // Get the filtered lists for top and side textures const topTexturesList = getMaterialMaps(topMaterial, defaultMaterialMap); const sideTexturesList = getMaterialMaps(sideMaterial, defaultMaterialMap); - // Use loader to load top and side textures const [topTexture, topNormalTexture, topRoughnessTexture, topMetalicTexture] = useLoader(TextureLoader, topTexturesList); const [sideTexture, sideNormalTexture, sideRoughnessTexture, sideMetalicTexture] = useLoader(TextureLoader, sideTexturesList); - // Early exit if materials are missing - if (!materials[floor.topMaterial] || !materials[floor.sideMaterial]) return null; - - // Combine and pair textures with their corresponding material const textureMaterialMap = [ { textures: [ @@ -131,7 +130,6 @@ function FloorInstance({ floor }: { floor: Floor }) { }, ]; - // Apply texture settings textureMaterialMap.forEach(({ textures, materialKey }) => { const tileScale = materials[materialKey]?.textureTileScale ?? [ textureScale, @@ -143,20 +141,36 @@ function FloorInstance({ floor }: { floor: Floor }) { tex.wrapS = tex.wrapT = RepeatWrapping; tex.repeat.set(tileScale[0], tileScale[1]); tex.anisotropy = 16; - // First texture is always the color map (use SRGB), others should be linear tex.colorSpace = idx < 1 ? SRGBColorSpace : NoColorSpace; }); }); - if (!shape) return null; + const geometry = useMemo(() => { + if (!shapeData) return null; + return new ExtrudeGeometry(shapeData.shape, { + depth: !floor.isBeveled ? floor.floorDepth : floor.floorDepth - 0.1, + bevelEnabled: floor.isBeveled, + bevelSegments: floor.bevelStrength, + bevelOffset: -0.1, + bevelSize: 0.1, + bevelThickness: 0.1, + }); + }, [shapeData, floor]); + + if (!geometry) return null; return ( { if (!toggleView && activeModule === "builder") { @@ -173,41 +187,32 @@ function FloorInstance({ floor }: { floor: Floor }) { } }} > - - - - + + + + {floor.decals.map((decal) => ( + + ))} ); } diff --git a/app/src/modules/builder/wall/Instances/instance/wall.tsx b/app/src/modules/builder/wall/Instances/instance/wall.tsx index 71ccdd4..67d5b8a 100644 --- a/app/src/modules/builder/wall/Instances/instance/wall.tsx +++ b/app/src/modules/builder/wall/Instances/instance/wall.tsx @@ -17,7 +17,7 @@ import material1 from '../../../../../assets/textures/floor/factory wall texture function Wall({ wall }: { readonly wall: Wall }) { const { wallStore, wallAssetStore } = useSceneContext(); - const { walls, addDecal } = wallStore(); + const { walls } = wallStore(); const { wallAssets, getWallAssetsByWall, setVisibility } = wallAssetStore(); const assets = getWallAssetsByWall(wall.wallUuid); const { selectedWall, setSelectedWall, setSelectedDecal } = useBuilderStore(); @@ -118,6 +118,7 @@ function Wall({ wall }: { readonly wall: Wall }) { > {(assets.length > 0 || (walls[0].wallUuid === wall.wallUuid && wallAssets.length > 0)) ? 0) return; - const decal: Decal = { - decalUuid: THREE.MathUtils.generateUUID(), - decalName: 'Decal', - decalId: "68abe2f771863f0888b0d35d", - decalPosition: [0, 0, wall.wallThickness / 2 + 0.001], - decalRotation: 0, - decalOpacity: 1, - decalScale: 1, - decalType: { type: 'Wall', wallUuid: wall.wallUuid } - } - addDecal(wall.wallUuid, decal); } } }} diff --git a/app/src/modules/scene/postProcessing/postProcessing.tsx b/app/src/modules/scene/postProcessing/postProcessing.tsx index 555c7a3..24f7428 100644 --- a/app/src/modules/scene/postProcessing/postProcessing.tsx +++ b/app/src/modules/scene/postProcessing/postProcessing.tsx @@ -165,7 +165,7 @@ export default function PostProcessing() { )} {selectedDecal && ( void; // Setters - Decal - setSelectedDecal: (decal: { decalMesh: Object3D, decalData: Decal } | null) => void; + setSelectedDecal: (decal: { decalMesh: Object3D | null, decalData: Decal } | null) => void; setDeletableDecal: (decal: Object3D | null) => void; + setDecalDragState: (isDragging: boolean, draggingDecalUuid: string | null, dragOffset: Vector3 | null) => void; // Setters - Aisle General setSelectedAisle: (aisle: Object3D | null) => void; @@ -143,6 +149,11 @@ export const useBuilderStore = create()( selectedDecal: null, deletableDecal: null, + decalDragState: { + isDragging: false, + draggingDecalUuid: null, + dragOffset: null, + }, selectedAisle: null, aisleType: 'solid-aisle', @@ -290,7 +301,7 @@ export const useBuilderStore = create()( // === Setters: Decal === - setSelectedDecal: (decal: { decalMesh: Object3D, decalData: Decal } | null) => { + setSelectedDecal: (decal: { decalMesh: Object3D | null, decalData: Decal } | null) => { set((state) => { state.selectedDecal = decal; }) @@ -302,6 +313,16 @@ export const useBuilderStore = create()( }) }, + setDecalDragState: (isDragging: boolean, draggingDecalUuid: string | null, dragOffset: Vector3 | null) => { + set((state) => { + state.decalDragState = { + isDragging, + draggingDecalUuid, + dragOffset, + } + }) + }, + // === Setters: Aisle General === setSelectedAisle: (aisle: Object3D | null) => { diff --git a/app/src/store/builder/useFloorStore.ts b/app/src/store/builder/useFloorStore.ts index c7b9545..e875c78 100644 --- a/app/src/store/builder/useFloorStore.ts +++ b/app/src/store/builder/useFloorStore.ts @@ -19,8 +19,8 @@ interface FloorStore { setBevelStrength: (uuid: string, strength: number) => void; setDepth: (uuid: string, depth: number) => void; setMaterial: (uuid: string, sideMaterial: string, topMaterial: string) => void; - addDecal: (floors: string, decal: Decal) => void; - updateDecal: (decalUuid: string, decal: Decal) => Floor | undefined; + addDecal: (floors: string, decal: Decal) => Decal | undefined; + updateDecal: (decalUuid: string, decal: Partial) => Floor | undefined; removeDecal: (decalUuid: string) => Floor | undefined; updateDecalPosition: (decalUuid: string, position: [number, number, number]) => void; updateDecalRotation: (decalUuid: string, rotation: number) => void; @@ -196,12 +196,17 @@ export const createFloorStore = () => { } }), - addDecal: (floorUuid, decal) => set(state => { - const floor = state.floors.find(f => f.floorUuid === floorUuid); - if (floor) { - floor.decals.push(decal); - } - }), + addDecal: (floorUuid, decal) => { + let addedDecal: Decal | undefined; + set(state => { + const floor = state.floors.find(f => f.floorUuid === floorUuid); + if (floor) { + floor.decals.push(decal); + addedDecal = JSON.parse(JSON.stringify(decal)); + } + }) + return addedDecal; + }, updateDecal: (decalUuid, updatedDecal) => { let affectedFloor: Floor | undefined; @@ -209,12 +214,12 @@ export const createFloorStore = () => { for (const floor of state.floors) { const index = floor.decals.findIndex(d => d.decalUuid === decalUuid); if (index !== -1) { - floor.decals[index] = updatedDecal; + Object.assign(floor.decals[index], updatedDecal); affectedFloor = JSON.parse(JSON.stringify(floor)); break; } } - }) + }); return affectedFloor; }, diff --git a/app/src/store/builder/useWallStore.ts b/app/src/store/builder/useWallStore.ts index c7c07e9..705e611 100644 --- a/app/src/store/builder/useWallStore.ts +++ b/app/src/store/builder/useWallStore.ts @@ -9,8 +9,8 @@ interface WallStore { removeWall: (uuid: string) => void; clearWalls: () => void; removeWallByPoints: (Points: [Point, Point]) => Wall | undefined; - addDecal: (wallUuid: string, decal: Decal) => void; - updateDecal: (decalUuid: string, decal: Decal) => Wall | undefined; + addDecal: (wallUuid: string, decal: Decal) => Decal | undefined; + updateDecal: (decalUuid: string, decal: Partial) => Wall | undefined; removeDecal: (decalUuid: string) => Wall | undefined; updateDecalPosition: (decalUuid: string, position: [number, number, number]) => Wall | undefined; updateDecalRotation: (decalUuid: string, rotation: number) => void; @@ -83,12 +83,17 @@ export const createWallStore = () => { return removedWall; }, - addDecal: (wallUuid, decal) => set((state) => { - const wallToUpdate = state.walls.find(w => w.wallUuid === wallUuid); - if (wallToUpdate) { - wallToUpdate.decals.push(decal); - } - }), + addDecal: (wallUuid, decal) => { + let addedDecal: Decal | undefined; + set((state) => { + const wallToUpdate = state.walls.find(w => w.wallUuid === wallUuid); + if (wallToUpdate) { + wallToUpdate.decals.push(decal); + addedDecal = JSON.parse(JSON.stringify(decal)); + } + }) + return addedDecal; + }, updateDecal: (decalUuid, decal) => { let affectedWall: Wall | undefined; From c4d1f90ee7cd0322de219d1b038f4eb4b47a91fc Mon Sep 17 00:00:00 2001 From: Vishnu Date: Thu, 28 Aug 2025 15:51:48 +0530 Subject: [PATCH 17/34] refactor: enhance asset search UI and improve styling for headers and buttons --- .../components/layout/sidebarLeft/Assets.tsx | 27 +++++++--- app/src/styles/layout/sidebar.scss | 49 ++++++++++++++++--- 2 files changed, 61 insertions(+), 15 deletions(-) diff --git a/app/src/components/layout/sidebarLeft/Assets.tsx b/app/src/components/layout/sidebarLeft/Assets.tsx index 824062f..47e797a 100644 --- a/app/src/components/layout/sidebarLeft/Assets.tsx +++ b/app/src/components/layout/sidebarLeft/Assets.tsx @@ -2,7 +2,11 @@ import React, { useEffect, useState } from "react"; import Search from "../../ui/inputs/Search"; import { getCategoryAsset } from "../../../services/factoryBuilder/asset/assets/getCategoryAsset"; import { fetchAssets } from "../../../services/marketplace/fetchAssets"; -import { useDecalStore, useDroppedDecal, useSelectedItem } from "../../../store/builder/store"; +import { + useDecalStore, + useDroppedDecal, + useSelectedItem, +} from "../../../store/builder/store"; // images ------------------- import vehicle from "../../../assets/image/categories/vehicles.png"; @@ -17,6 +21,7 @@ import decal from "../../../assets/image/categories/decal.png"; import SkeletonUI from "../../templates/SkeletonUI"; import { AlertIcon, + ArrowIcon, DecalInfoIcon, HangTagIcon, NavigationIcon, @@ -164,7 +169,9 @@ const Assets: React.FC = () => {
-

Results for {searchValue}

+

+ Results for '{searchValue}' +

{categoryAssets?.map((asset: any, index: number) => ( @@ -210,7 +217,7 @@ const Assets: React.FC = () => { if (selectedCategory) { return (
-

+

{selectedCategory}

@@ -231,8 +241,9 @@ const Assets: React.FC = () => { {activeSubcategories.map((cat, index) => (
{ fetchCategoryDecals(cat.name); setSelectedSubCategory(cat.name); @@ -308,7 +319,7 @@ const Assets: React.FC = () => { category: asset.category, decalName: asset.decalName, decalImage: asset.decalImage, - decalId: asset.id + decalId: asset.id, }); }} /> @@ -337,7 +348,7 @@ const Assets: React.FC = () => { return (
-

Categories

+

Categories

{Array.from( new Set(categoryList.map((asset) => asset.category)) diff --git a/app/src/styles/layout/sidebar.scss b/app/src/styles/layout/sidebar.scss index aa8e84e..e3dd706 100644 --- a/app/src/styles/layout/sidebar.scss +++ b/app/src/styles/layout/sidebar.scss @@ -1984,17 +1984,51 @@ max-height: 50vh; overflow: auto; - h2, - .searched-content { + .header, + .searched-content, + .categories-header { color: var(--text-color); - font-family: $large; font-weight: $bold-weight; - padding: 8px; + padding: 4px 8px; @include flex-space-between; - - .back-button { - cursor: pointer; + position: sticky; + top: 0; + z-index: 4; + background: var(--background-color); + border-radius: 0 0 12px 12px; + backdrop-filter: blur(4px); + .search-for{ + display: inline-block; + color: var(--accent-color); + max-width: 238px; + overflow: hidden; + text-overflow: ellipsis; } + .back-button { + padding: 4px 12px; + border-radius: 20px; + transition: background 0.2s; + display: flex; + align-items: center; + .back-arrow { + margin-right: 2px; + transform: translateX(0) translateY(-1px) scale(1.6) rotate(90deg); + transition: all 0.2s; + } + &:hover { + background: var(--background-color-solid); + .back-arrow { + transform: translateX(-2px) translateY(-1px) scale(1.6) + rotate(90deg); + } + } + } + } + + .categories-header, + .searched-content { + position: relative; + padding: 8px; } .categories-container { @@ -2132,6 +2166,7 @@ border: 1px solid #564b69; padding: 12px 10px; border-radius: 15px; + margin-bottom: 4px; .catogory-asset-filter-wrapper { display: flex; From 92b1ba61973268f5232a5406fbd83bfbb0473aff Mon Sep 17 00:00:00 2001 From: Jerald-Golden-B Date: Thu, 28 Aug 2025 16:01:16 +0530 Subject: [PATCH 18/34] decal bug fix --- app/src/modules/builder/Decal/decalCreator/decalCreator.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/src/modules/builder/Decal/decalCreator/decalCreator.tsx b/app/src/modules/builder/Decal/decalCreator/decalCreator.tsx index 221a2bf..35b54ce 100644 --- a/app/src/modules/builder/Decal/decalCreator/decalCreator.tsx +++ b/app/src/modules/builder/Decal/decalCreator/decalCreator.tsx @@ -15,7 +15,7 @@ import { getUserData } from '../../../../functions/getUserData'; function DecalCreator() { const { wallStore, floorStore } = useSceneContext(); const { addDecal: addDecalOnWall, getWallById } = wallStore(); - const { addDecal : addDecalOnFloor, getFloorById } = floorStore(); + const { addDecal: addDecalOnFloor, getFloorById } = floorStore(); const { droppedDecal } = useDroppedDecal(); const { activeModule } = useModuleStore(); const { userId, organization } = getUserData(); @@ -36,6 +36,7 @@ function DecalCreator() { const intersects = raycaster.intersectObjects(scene.children, true); const wallIntersect = intersects.find(i => i.object.userData && i.object.userData.wallUuid); const floorIntersect = intersects.find(i => i.object.userData && i.object.userData.floorUuid); + console.log('wallIntersect: ', wallIntersect); if (wallIntersect) { const wall = getWallById(wallIntersect.object.userData.wallUuid); @@ -51,7 +52,7 @@ function DecalCreator() { type: 'Wall', wallUuid: wallIntersect.object.userData.wallUuid, }, - decalPosition: [point.x, point.y, wall.wallThickness / 2 + 0.001], + decalPosition: [point.x, point.y, (wall.wallThickness / 2 + 0.001) * (wallIntersect.normal?.z || 1)], decalRotation: 0, decalOpacity: 1, decalScale: 1, From cb87cd067b5381ef12f3345c64452019efe48f5b Mon Sep 17 00:00:00 2001 From: Jerald-Golden-B Date: Thu, 28 Aug 2025 16:32:45 +0530 Subject: [PATCH 19/34] added decal fallback --- .../assets/image/fallback/fallback decal 1.png | Bin 0 -> 19237 bytes .../assets/image/fallback/fallback decal.png | Bin 0 -> 2168 bytes .../Decal/decalInstance/decalInstance.tsx | 7 ++++--- 3 files changed, 4 insertions(+), 3 deletions(-) create mode 100644 app/src/assets/image/fallback/fallback decal 1.png create mode 100644 app/src/assets/image/fallback/fallback decal.png diff --git a/app/src/assets/image/fallback/fallback decal 1.png b/app/src/assets/image/fallback/fallback decal 1.png new file mode 100644 index 0000000000000000000000000000000000000000..b47f4081ed66cb815cf3fcbed4ed4aabd033e83f GIT binary patch literal 19237 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 M07*qoM6N<$f*~=E_5c6? literal 0 HcmV?d00001 diff --git a/app/src/assets/image/fallback/fallback decal.png b/app/src/assets/image/fallback/fallback decal.png new file mode 100644 index 0000000000000000000000000000000000000000..b30e9fceb33ca45dbd3e03a326271f6478fc1543 GIT binary patch literal 2168 zcmbuB=Q|q;8^(i#*hHl291>eqNQ_#ih;T}W+G^C^RP9wowNa{ym^E9o_6`nODxDVE}-JWOlkE2mktl|t1ydV3aHzS+kJHmpx>8bdMBsI-!L3g{A!tGTPY+WiUq?>M| zEs~Wsv2QQ+UK$b*FlTV^m>Ddh-FWfj;h{QOdQ?6+`V-lnHsw;c^Bv{65sTUDMh!jD zifj$iy0|&kN4jsxsX$99bDVOSJKH_=t{Nz|FPw9;ijut7*D!cOR^nKKd8T(`3p6B2 zuX9U`ar|jYGQ8hnrQ=|_=w`&L_aI{qWW19H28v}b60qhWe)(TkixR_X0cyDcA#Z$g z2T}j@c^|oGXpzoF@{igv!c7sU*6euF-{yqsyhu)9B$VsGC^V~(Q4<~<5*9vov-5-y zpFy?`!rz|QC9?`eX_hT7N;L+k!ARnDE5hzAie5bHs4Ou;d=BZiHM=xh08BP4H?<0B^~Uct4^(v~D7 zWqvwMASm@W?_70pqSKz9PIG{(g)Hz{ryatgM;c;U+zc(uJZ*%08qp#kfzJerv1iRF zMUmD=9p~X3=k7U67WGovDwz-{ZF_MwVBVb2iQsW?axsck@l_F=RM#(Eyx}etM@%Z1 zLm-i0pa5LBgq)h~qF#xx64q*ce=d!*e=F7=CG%6q#)4<7GqoowM^MP*_Uk5>DI6#= zKjevCd%d&Y@_7jLvqTrU&_;oo9NX483igyTieAD~hA>6a> z-y&p%kFzZR)X`BQO4NCWs~^trYRUC2jU65n+L_3hW?dmlBZh};Exg3O$0AIzAu*bU zP7!x?{nd*Jv$WNGHMD}Ryj|Ty`KXkrSy#$xcdi3kQ-bzxx|=zm3#Yh+7nNnUYo0hB zDsbcSv((>q!2IW5#ZJ?wk|q<|UfKjp_NbMO-Z2UJ^Kj=HR11r9Fk| z0ZIN0>bUDwThEXLP wxY395jxvpJ)Yb9nF^oTf;>JoP`bmHl%DHH#GiF5yI!b$0eAM;6Cy3dD9l{D<)hWlmY!mS2h|tr*RA^i;y8W}|JS5&yD~ zEW0?{hC=vtC#wu?*y!KL(c~&wfDjuvux!0q7e1;Jg67PmI*&wEKH(Z=$Pa6tt%K*w zyf>rwiJ@y%jLhX9f?aMS4hSitU);T^}Jw(f(BMy)m(L1&Sc)R z1G+rO@T{H7c=jbn@mgasH$pYXc7@1`cW$ zVw5-Sd(0)uNaVh`SlO0>;Fwo0fA5?>88Sf3h1i<6xz;c#kr>RQvI*vuyH3*b@y(~H z=1C-RPmkdAXxuoBdw=ja@VcA1y|cB+a%#@<%&9-uat*DLExYTzZ$*e}Ucn?|t(Tim z8lEddcM^f+gkP^zu~%L7 zId{tFpHzLgh=o6R1$U02sX2-cg7+cQP4k>A;ndJX(M#=BpCaJd)%n% zUySEv!b=tDcnIB066;u-X<0kqDp!?51In>wqM=EHenHoTNSRnN{}l>MKdDZBphANu zMXy?E>WW*X-&?E5vZt2^ZIgM1-ihOWop7LsH8IG_J% z6)xk~+;5lXpp<4dd0Zu{Nq26cYR?MT@JGmeI>+bvw yj-SZqTe&3_+=Q;jzwPy73%)v_c6dV1Sk8KL=Jnc(cP<|VfPtQ&Zmo_(?0*4CZT#*4 literal 0 HcmV?d00001 diff --git a/app/src/modules/builder/Decal/decalInstance/decalInstance.tsx b/app/src/modules/builder/Decal/decalInstance/decalInstance.tsx index 8d0540a..f31dddd 100644 --- a/app/src/modules/builder/Decal/decalInstance/decalInstance.tsx +++ b/app/src/modules/builder/Decal/decalInstance/decalInstance.tsx @@ -4,9 +4,10 @@ import { useToggleView, useToolMode } from '../../../../store/builder/store'; import { useBuilderStore } from '../../../../store/builder/useBuilderStore'; import { retrieveImage, storeImage } from '../../../../utils/indexDB/idbUtils'; -import defaultMaterial from '../../../../assets/textures/floor/wall-tex.png'; +import defaultMaterial from '../../../../assets/image/fallback/fallback decal 1.png'; import useModuleStore from '../../../../store/useModuleStore'; import { useEffect, useRef, useState } from 'react'; + import { useDecalEventHandlers } from '../eventHandler/useDecalEventHandlers'; // import { upsertWallApi } from '../../../../services/factoryBuilder/wall/upsertWallApi'; @@ -111,9 +112,9 @@ function DecalInstance({ parent, visible = true, decal, zPosition = decal.decalP console.error("Error storing texture in IndexedDB:", error); } }, - undefined, + undefined, (error) => { - console.error(`Error loading texture from backend:`, error); + echo.error(`Error loading texture from backend: ${decal.decalName}`); loadDefaultTexture(); } ); From 94bec4f2f0e36bbd7499fb9ad81e506a3dfa650e Mon Sep 17 00:00:00 2001 From: Jerald-Golden-B Date: Thu, 28 Aug 2025 18:00:00 +0530 Subject: [PATCH 20/34] added selectedAisleProperties and its backend updation --- .../layout/sidebarRight/SideBarRight.tsx | 32 +- .../properties/SelectedAisleProperties.tsx | 545 ++++++++++++++++++ .../properties/SelectedDecalProperties.tsx | 23 +- .../instance/aisleTypes/arcAisle.tsx | 14 +- .../instance/aisleTypes/arrowAisle.tsx | 14 +- .../instance/aisleTypes/arrowsAisle.tsx | 14 +- .../instance/aisleTypes/circleAisle.tsx | 14 +- .../instance/aisleTypes/dashedAisle.tsx | 14 +- .../instance/aisleTypes/dottedAisle.tsx | 14 +- .../instance/aisleTypes/junctionAisle.tsx | 14 +- .../instance/aisleTypes/solidAisle.tsx | 15 +- .../scene/postProcessing/postProcessing.tsx | 2 +- app/src/store/builder/useAisleStore.ts | 149 +++-- app/src/store/builder/useBuilderStore.ts | 6 +- 14 files changed, 765 insertions(+), 105 deletions(-) create mode 100644 app/src/components/layout/sidebarRight/properties/SelectedAisleProperties.tsx diff --git a/app/src/components/layout/sidebarRight/SideBarRight.tsx b/app/src/components/layout/sidebarRight/SideBarRight.tsx index ae9c908..5e283c9 100644 --- a/app/src/components/layout/sidebarRight/SideBarRight.tsx +++ b/app/src/components/layout/sidebarRight/SideBarRight.tsx @@ -6,7 +6,7 @@ import { useToggleStore } from "../../../store/useUIToggleStore"; import Visualization from "./visualization/Visualization"; import Analysis from "./analysis/Analysis"; import Simulations from "./simulation/Simulations"; -import useVersionHistoryVisibleStore, { useDecalStore, useSaveVersion, useSelectedFloorItem, useToolMode, } from "../../../store/builder/store"; +import useVersionHistoryVisibleStore, { useSaveVersion, useSelectedFloorItem, useToolMode } from "../../../store/builder/store"; import { useSelectedEventData, useSelectedEventSphere, } from "../../../store/simulation/useSimulationStore"; import { useBuilderStore } from "../../../store/builder/useBuilderStore"; import GlobalProperties from "./properties/GlobalProperties"; @@ -19,8 +19,9 @@ import WallProperties from "./properties/WallProperties"; import FloorProperties from "./properties/FloorProperties"; import SelectedWallProperties from "./properties/SelectedWallProperties"; import SelectedFloorProperties from "./properties/SelectedFloorProperties"; -import ResourceManagement from "./resourceManagement/ResourceManagement"; import SelectedDecalProperties from "./properties/SelectedDecalProperties"; +import SelectedAisleProperties from "./properties/SelectedAisleProperties"; +import ResourceManagement from "./resourceManagement/ResourceManagement"; type DisplayComponent = | "versionHistory" @@ -31,12 +32,13 @@ type DisplayComponent = | "assetProperties" | "selectedWallProperties" | "selectedFloorProperties" + | "selectedDecalProperties" + | "selectedAisleProperties" | "zoneProperties" | "simulations" | "mechanics" | "analysis" | "visualization" - | "selectedDecalProperties" | "resourceManagement" | "none"; @@ -110,23 +112,27 @@ const SideBarRight: React.FC = () => { setDisplayComponent("assetProperties"); return; } - if (!selectedFloorItem && !selectedFloor && !selectedAisle && selectedWall) { + if (!selectedFloorItem && !selectedFloor && !selectedAisle && !selectedDecal && selectedWall) { setDisplayComponent("selectedWallProperties"); return; } - if (!selectedFloorItem && !selectedWall && !selectedAisle && selectedFloor) { + if (!selectedFloorItem && !selectedWall && !selectedAisle && !selectedDecal && selectedFloor) { setDisplayComponent("selectedFloorProperties"); return; } - if (viewVersionHistory) { + if (viewVersionHistory && !selectedFloorItem && !selectedWall && !selectedAisle && !selectedFloor && !selectedDecal) { setDisplayComponent("versionHistory"); return; } - if (selectedDecal) { + if (!selectedFloorItem && !selectedFloor && !selectedAisle && !selectedWall && selectedDecal) { setDisplayComponent("selectedDecalProperties"); return; } - if (!selectedFloorItem && !selectedFloor && !selectedWall && !selectedDecal) { + if (!selectedFloorItem && !selectedFloor && !selectedWall && !selectedDecal && selectedAisle) { + setDisplayComponent("selectedAisleProperties"); + return; + } + if (!selectedFloorItem && !selectedFloor && !selectedWall && !selectedDecal && !selectedAisle) { if (toolMode === "Aisle") { setDisplayComponent("aisleProperties"); return; @@ -166,12 +172,16 @@ const SideBarRight: React.FC = () => { return ; case "assetProperties": return ; + case "zoneProperties": + return ; case "selectedWallProperties": return ; case "selectedFloorProperties": return ; - case "zoneProperties": - return ; + case "selectedDecalProperties": + return ; + case "selectedAisleProperties": + return ; case "simulations": return ; case "mechanics": @@ -180,8 +190,6 @@ const SideBarRight: React.FC = () => { return ; case "visualization": return ; - case "selectedDecalProperties": - return ; case "resourceManagement": return ; default: diff --git a/app/src/components/layout/sidebarRight/properties/SelectedAisleProperties.tsx b/app/src/components/layout/sidebarRight/properties/SelectedAisleProperties.tsx new file mode 100644 index 0000000..c1e1d24 --- /dev/null +++ b/app/src/components/layout/sidebarRight/properties/SelectedAisleProperties.tsx @@ -0,0 +1,545 @@ +import React, { useEffect, useState } from "react"; +import { useParams } from "react-router-dom"; +import InputWithDropDown from "../../../ui/inputs/InputWithDropDown"; +import { ArrowIcon } from "../../../icons/ExportCommonIcons"; + +// image imports +import Arc from "../../../../assets/image/aisleTypes/Arc.png"; +import Arrow from "../../../../assets/image/aisleTypes/Arrow.png"; +import Arrows from "../../../../assets/image/aisleTypes/Arrows.png"; +import Circle from "../../../../assets/image/aisleTypes/Circle.png"; +import Dashed from "../../../../assets/image/aisleTypes/Dashed.png"; +import Directional from "../../../../assets/image/aisleTypes/Directional.png"; +import Dotted from "../../../../assets/image/aisleTypes/Dotted.png"; +import Solid from "../../../../assets/image/aisleTypes/Solid.png"; +import InputToggle from "../../../ui/inputs/InputToggle"; +import { useBuilderStore } from "../../../../store/builder/useBuilderStore"; +import { useSceneContext } from "../../../../modules/scene/sceneContext"; +import { useVersionContext } from "../../../../modules/builder/version/versionContext"; +import { useSocketStore } from "../../../../store/builder/store"; +import { getUserData } from "../../../../functions/getUserData"; + +// import { upsertAisleApi } from "../../../../services/factoryBuilder/aisle/upsertAisleApi"; + +interface TextureItem { + color: string; + id: AisleColors; + brief: string; + texture: string; +} + +const SelectedAisleProperties: React.FC = () => { + const [collapsePresets, setCollapsePresets] = useState(false); + const [collapseTexture, setCollapseTexture] = useState(true); + const { aisleStore } = useSceneContext(); + const { getAisleById, updateAisle, setDashedAisleProperties, setDottedAisleProperties, setArrowsAisleProperties, setArcAisleWidth, setColor } = aisleStore(); + const { selectedVersionStore } = useVersionContext(); + const { selectedVersion } = selectedVersionStore(); + const { socket } = useSocketStore(); + const { userId, organization } = getUserData(); + const { projectId } = useParams(); + const [selectedAisleData, setSelectedAisleData] = useState(); + + const { selectedAisle, setSelectedAisle } = useBuilderStore(); + + useEffect(() => { + const aisleData = getAisleById(selectedAisle?.aisleMesh?.uuid || ""); + setSelectedAisleData(aisleData); + }, [selectedAisle, getAisleById]); + + if (!selectedAisleData) return null; + + const updateBackend = (updatedAisle: Aisle) => { + if (updatedAisle && projectId) { + + // API + + // upsertAisleApi(updatedAisle.aisleUuid, updatedAisle.points, updatedAisle.type, projectId, selectedVersion?.versionId || ''); + + // SOCKET + + const data = { + projectId: projectId, + versionId: selectedVersion?.versionId || '', + userId: userId, + organization: organization, + aisleUuid: updatedAisle.aisleUuid, + points: updatedAisle.points, + type: updatedAisle.type + } + + socket.emit('v1:model-aisle:add', data); + } + } + + const aisleTextureList: TextureItem[] = [ + { color: "yellow", id: "yellow", brief: "pedestrian walkways", texture: "" }, + { color: "gray", id: "gray", brief: "basic", texture: "" }, + { color: "green", id: "green", brief: "pedestrian walkways", texture: "" }, + { color: "orange", id: "orange", brief: "material flow", texture: "" }, + { color: "blue", id: "blue", brief: "vehicle paths", texture: "" }, + { color: "purple", id: "purple", brief: "material flow", texture: "" }, + { color: "red", id: "red", brief: "safety zone", texture: "" }, + { color: "bright green", id: "#66FF00", brief: "safety zone", texture: "" }, + { color: "yellow-black", id: "yellow-black", brief: "utility aisles", texture: "" }, + { color: "white-black", id: "white-black", brief: "utility aisles", texture: "" }, + ]; + + const aisleTypes: { + name: string; + type: AisleTypes; + id: string; + thumbnail: string; + }[] = [ + { name: "Solid", type: "solid-aisle", id: "1", thumbnail: Solid }, + { name: "Dotted", type: "dotted-aisle", id: "2", thumbnail: Dotted }, + { name: "Dashed", type: "dashed-aisle", id: "3", thumbnail: Dashed }, + { name: "Arrow", type: "arrow-aisle", id: "4", thumbnail: Arrow }, + { name: "Continuous Arrows", type: "arrows-aisle", id: "5", thumbnail: Arrows }, + { name: "Directional", type: "junction-aisle", id: "6", thumbnail: Directional }, + { name: "Arc", type: "arc-aisle", id: "7", thumbnail: Arc }, + { name: "Circle", type: "circle-aisle", id: "8", thumbnail: Circle }, + ]; + + const createAisleTypeObject = (newType: AisleTypes, currentType: AisleType): AisleType => { + switch (newType) { + case 'solid-aisle': + return { + aisleType: 'solid-aisle', + aisleColor: currentType.aisleColor, + aisleWidth: 'aisleWidth' in currentType ? currentType.aisleWidth : 0.1 + } as SolidAisle; + + case 'dashed-aisle': + return { + aisleType: 'dashed-aisle', + aisleColor: currentType.aisleColor, + aisleWidth: 'aisleWidth' in currentType ? currentType.aisleWidth : 0.1, + dashLength: 'dashLength' in currentType ? (currentType as DashedAisle).dashLength : 0.5, + gapLength: 'gapLength' in currentType ? (currentType as DashedAisle).gapLength : 0.3 + } as DashedAisle; + + case 'dotted-aisle': + return { + aisleType: 'dotted-aisle', + aisleColor: currentType.aisleColor, + dotRadius: 'dotRadius' in currentType ? (currentType as DottedAisle).dotRadius : 0.1, + gapLength: 'gapLength' in currentType ? (currentType as DottedAisle).gapLength : 0.3 + } as DottedAisle; + + case 'arrow-aisle': + return { + aisleType: 'arrow-aisle', + aisleColor: currentType.aisleColor, + aisleWidth: 'aisleWidth' in currentType ? currentType.aisleWidth : 0.1 + } as ArrowAisle; + + case 'arrows-aisle': + return { + aisleType: 'arrows-aisle', + aisleColor: currentType.aisleColor, + aisleWidth: 'aisleWidth' in currentType ? currentType.aisleWidth : 0.1, + aisleLength: 'aisleLength' in currentType ? (currentType as ArrowsAisle).aisleLength : 0.6, + gapLength: 'gapLength' in currentType ? (currentType as ArrowsAisle).gapLength : 0.3 + } as ArrowsAisle; + + case 'arc-aisle': + return { + aisleType: 'arc-aisle', + aisleColor: currentType.aisleColor, + aisleWidth: 'aisleWidth' in currentType ? currentType.aisleWidth : 0.1, + isFlipped: 'isFlipped' in currentType ? (currentType as ArcAisle).isFlipped : false + } as ArcAisle; + + case 'circle-aisle': + return { + aisleType: 'circle-aisle', + aisleColor: currentType.aisleColor, + aisleWidth: 'aisleWidth' in currentType ? currentType.aisleWidth : 0.1 + } as CircleAisle; + + case 'junction-aisle': + return { + aisleType: 'junction-aisle', + aisleColor: currentType.aisleColor, + aisleWidth: 'aisleWidth' in currentType ? currentType.aisleWidth : 0.1, + isFlipped: 'isFlipped' in currentType ? (currentType as JunctionAisle).isFlipped : false + } as JunctionAisle; + + default: + return { + aisleType: 'solid-aisle', + aisleColor: currentType.aisleColor, + aisleWidth: 0.1 + } as SolidAisle; + } + }; + + const handleAisleTypeChange = (newType: AisleTypes) => { + if (!selectedAisle?.aisleData) return; + const newAisleType = createAisleTypeObject(newType, selectedAisleData.type); + + const updatedAisle = updateAisle(selectedAisleData.aisleUuid, { + type: newAisleType + }); + + setSelectedAisle({ aisleData: selectedAisle.aisleData, aisleMesh: null }); + + setSelectedAisleData({ + ...selectedAisleData, + type: newAisleType + }); + + if (updatedAisle) { + updateBackend(updatedAisle); + } + }; + + const handleColorChange = (value: AisleColors) => { + const updatedAisle = setColor(selectedAisleData.aisleUuid, value); + + setSelectedAisleData({ + ...selectedAisleData, + type: { + ...selectedAisleData.type, + aisleColor: value + } + }) + + if (updatedAisle) { + updateBackend(updatedAisle); + } + }; + + const handleAisleWidthChange = (value: string) => { + const width = parseFloat(value); + if (!isNaN(width) && selectedAisleData.type.aisleType !== 'dotted-aisle') { + const updatedAisle = updateAisle(selectedAisleData.aisleUuid, { + type: { + ...selectedAisleData.type, + aisleWidth: width + } + }); + + setSelectedAisleData({ + ...selectedAisleData, + type: { + ...selectedAisleData.type, + aisleWidth: width + } + }) + + if (updatedAisle) { + updateBackend(updatedAisle); + } + } + }; + + const handleDashLengthChange = (value: string) => { + const length = parseFloat(value); + if (!isNaN(length) && selectedAisleData.type.aisleType === 'dashed-aisle') { + const updatedAisle = setDashedAisleProperties(selectedAisleData.aisleUuid, { + dashLength: length + }); + + setSelectedAisleData({ + ...selectedAisleData, + type: { + ...(selectedAisleData.type as DashedAisle), + dashLength: length + } + }) + + if (updatedAisle) { + updateBackend(updatedAisle); + } + } + }; + + const handleGapLengthChange = (value: string) => { + const length = parseFloat(value); + if (!isNaN(length) && (selectedAisleData.type.aisleType === 'dashed-aisle' || selectedAisleData.type.aisleType === 'dotted-aisle' || selectedAisleData.type.aisleType === 'arrows-aisle')) { + if (selectedAisleData.type.aisleType === 'dashed-aisle') { + const updatedAisle = setDashedAisleProperties(selectedAisleData.aisleUuid, { + gapLength: length + }); + + setSelectedAisleData({ + ...selectedAisleData, + type: { + ...(selectedAisleData.type as DashedAisle), + gapLength: length + } + }) + + if (updatedAisle) { + updateBackend(updatedAisle); + } + } else if (selectedAisleData.type.aisleType === 'dotted-aisle') { + const updatedAisle = setDottedAisleProperties(selectedAisleData.aisleUuid, { + gapLength: length + }); + + setSelectedAisleData({ + ...selectedAisleData, + type: { + ...(selectedAisleData.type as DottedAisle), + gapLength: length + } + }) + + if (updatedAisle) { + updateBackend(updatedAisle); + } + } else if (selectedAisleData.type.aisleType === 'arrows-aisle') { + const updatedAisle = setArrowsAisleProperties(selectedAisleData.aisleUuid, { + gapLength: length + }); + + setSelectedAisleData({ + ...selectedAisleData, + type: { + ...(selectedAisleData.type as ArrowsAisle), + gapLength: length + } + }) + + if (updatedAisle) { + updateBackend(updatedAisle); + } + } + } + }; + + const handleDotRadiusChange = (value: string) => { + const radius = parseFloat(value); + if (!isNaN(radius) && selectedAisleData.type.aisleType === 'dotted-aisle') { + const updatedAisle = setDottedAisleProperties(selectedAisleData.aisleUuid, { + dotRadius: radius + }); + + setSelectedAisleData({ + ...selectedAisleData, + type: { + ...(selectedAisleData.type as DottedAisle), + dotRadius: radius + } + }) + + if (updatedAisle) { + updateBackend(updatedAisle); + } + } + }; + + const handleAisleLengthChange = (value: string) => { + const length = parseFloat(value); + if (!isNaN(length) && selectedAisleData.type.aisleType === 'arrows-aisle') { + const updatedAisle = setArrowsAisleProperties(selectedAisleData.aisleUuid, { + aisleLength: length + }); + + setSelectedAisleData({ + ...selectedAisleData, + type: { + ...(selectedAisleData.type as ArrowsAisle), + aisleLength: length + } + }) + + if (updatedAisle) { + updateBackend(updatedAisle); + } + } + }; + + const handleIsFlippedChange = () => { + if (selectedAisleData.type.aisleType === 'arc-aisle' || selectedAisleData.type.aisleType === 'junction-aisle') { + const currentType = selectedAisleData.type as ArcAisle | JunctionAisle; + const currentFlipped = currentType.isFlipped || false; + const updatedAisle = setArcAisleWidth(selectedAisleData.aisleUuid, { + isFlipped: !currentFlipped + }); + + setSelectedAisleData({ + ...selectedAisleData, + type: { + ...currentType, + isFlipped: !currentFlipped + } + }) + + if (updatedAisle) { + updateBackend(updatedAisle); + } + } + }; + + const renderAdvancedProperties = () => { + switch (selectedAisleData.type.aisleType) { + case 'dashed-aisle': + const dashedType = selectedAisleData.type as DashedAisle; + return ( + <> + + + + ); + case 'dotted-aisle': + const dottedType = selectedAisleData.type as DottedAisle; + return ( + <> + + + + ); + case 'arrows-aisle': + const arrowsType = selectedAisleData.type as ArrowsAisle; + return ( + <> + + + + ); + case 'junction-aisle': + case 'arc-aisle': + const flippedType = selectedAisleData.type as ArcAisle | JunctionAisle; + return ( + + ); + default: + return null; + } + }; + + return ( +
+
Properties
+ + {/* Basic Properties */} +
+ {selectedAisleData.type.aisleType !== 'dotted-aisle' && + + } + {renderAdvancedProperties()} +
+ + {/* Presets */} +
+ + {!collapsePresets && ( +
+ {aisleTypes.map((val) => ( +
+ +
+ ))} +
+ )} +
+ + {/* Texture */} +
+ + + {collapseTexture && ( +
+ {aisleTextureList.map((val) => ( + + ))} +
+ )} +
+
+ ); +}; + +export default SelectedAisleProperties; \ No newline at end of file diff --git a/app/src/components/layout/sidebarRight/properties/SelectedDecalProperties.tsx b/app/src/components/layout/sidebarRight/properties/SelectedDecalProperties.tsx index 99c16da..e605363 100644 --- a/app/src/components/layout/sidebarRight/properties/SelectedDecalProperties.tsx +++ b/app/src/components/layout/sidebarRight/properties/SelectedDecalProperties.tsx @@ -5,7 +5,6 @@ import { useBuilderStore } from "../../../../store/builder/useBuilderStore"; import { LayeringBottomIcon, LayeringTopIcon } from "../../../icons/ExportCommonIcons"; import { useSocketStore } from "../../../../store/builder/store"; import InputRange from "../../../ui/inputs/InputRange"; -import RotationInput from "../customInput/RotationInput"; import { getUserData } from "../../../../functions/getUserData"; // import { upsertWallApi } from "../../../../services/factoryBuilder/wall/upsertWallApi"; @@ -134,18 +133,22 @@ const SelectedDecalProperties = () => {
Decal Properties
- { handleRotationChange(parseFloat(e)) }} + handleRotationChange(value)} /> - { handleScaleChange(parseFloat(e)) }} + + handleScaleChange(value)} />
diff --git a/app/src/modules/builder/aisle/Instances/instance/aisleTypes/arcAisle.tsx b/app/src/modules/builder/aisle/Instances/instance/aisleTypes/arcAisle.tsx index bc09b68..d791f8f 100644 --- a/app/src/modules/builder/aisle/Instances/instance/aisleTypes/arcAisle.tsx +++ b/app/src/modules/builder/aisle/Instances/instance/aisleTypes/arcAisle.tsx @@ -1,5 +1,5 @@ import * as THREE from 'three'; -import { useMemo, useRef } from 'react'; +import { useEffect, useMemo, useRef } from 'react'; import { Extrude } from '@react-three/drei'; import * as Constants from '../../../../../../types/world/worldConstants'; import { useToolMode } from '../../../../../../store/builder/store'; @@ -8,7 +8,13 @@ import { useBuilderStore } from '../../../../../../store/builder/useBuilderStore function ArcAisle({ aisle }: { readonly aisle: Aisle }) { const aisleRef = useRef(null); const { toolMode } = useToolMode(); - const { setSelectedAisle, hoveredPoint } = useBuilderStore(); + const { setSelectedAisle, hoveredPoint, selectedAisle } = useBuilderStore(); + + useEffect(() => { + if (selectedAisle?.aisleData.aisleUuid === aisle.aisleUuid && !selectedAisle.aisleMesh) { + setSelectedAisle({ aisleData: selectedAisle.aisleData, aisleMesh: aisleRef.current }); + } + }, [selectedAisle]) const arc = useMemo(() => { if (aisle.points.length < 2 || aisle.type.aisleType !== 'arc-aisle') return null; @@ -63,8 +69,8 @@ function ArcAisle({ aisle }: { readonly aisle: Aisle }) { }, [aisle]); const handleClick = () => { - if (toolMode === 'move' && !hoveredPoint) { - setSelectedAisle(aisleRef.current); + if ((toolMode === 'move' || toolMode === 'cursor') && !hoveredPoint) { + setSelectedAisle({ aisleMesh: aisleRef.current, aisleData: aisle }); } } diff --git a/app/src/modules/builder/aisle/Instances/instance/aisleTypes/arrowAisle.tsx b/app/src/modules/builder/aisle/Instances/instance/aisleTypes/arrowAisle.tsx index 49bf666..115edc6 100644 --- a/app/src/modules/builder/aisle/Instances/instance/aisleTypes/arrowAisle.tsx +++ b/app/src/modules/builder/aisle/Instances/instance/aisleTypes/arrowAisle.tsx @@ -1,5 +1,5 @@ import * as THREE from 'three'; -import { useMemo, useRef } from 'react'; +import { useEffect, useMemo, useRef } from 'react'; import { Extrude } from '@react-three/drei'; import * as Constants from '../../../../../../types/world/worldConstants'; import { useToolMode } from '../../../../../../store/builder/store'; @@ -8,7 +8,13 @@ import { useBuilderStore } from '../../../../../../store/builder/useBuilderStore function ArrowAisle({ aisle }: { readonly aisle: Aisle }) { const aisleRef = useRef(null); const { toolMode } = useToolMode(); - const { setSelectedAisle, hoveredPoint } = useBuilderStore(); + const { setSelectedAisle, hoveredPoint, selectedAisle } = useBuilderStore(); + + useEffect(() => { + if (selectedAisle?.aisleData.aisleUuid === aisle.aisleUuid && !selectedAisle.aisleMesh) { + setSelectedAisle({ aisleData: selectedAisle.aisleData, aisleMesh: aisleRef.current }); + } + }, [selectedAisle]) const arrow = useMemo(() => { if (aisle.points.length < 2 || aisle.type.aisleType !== 'arrow-aisle') return null; @@ -50,8 +56,8 @@ function ArrowAisle({ aisle }: { readonly aisle: Aisle }) { }, [aisle]); const handleClick = () => { - if (toolMode === 'move' && !hoveredPoint) { - setSelectedAisle(aisleRef.current); + if ((toolMode === 'move' || toolMode === 'cursor') && !hoveredPoint) { + setSelectedAisle({ aisleMesh: aisleRef.current, aisleData: aisle }); } } diff --git a/app/src/modules/builder/aisle/Instances/instance/aisleTypes/arrowsAisle.tsx b/app/src/modules/builder/aisle/Instances/instance/aisleTypes/arrowsAisle.tsx index 6908cf3..3700d55 100644 --- a/app/src/modules/builder/aisle/Instances/instance/aisleTypes/arrowsAisle.tsx +++ b/app/src/modules/builder/aisle/Instances/instance/aisleTypes/arrowsAisle.tsx @@ -1,5 +1,5 @@ import * as THREE from 'three'; -import { useMemo, useRef } from 'react'; +import { useEffect, useMemo, useRef } from 'react'; import { Instances, Instance } from '@react-three/drei'; import * as Constants from '../../../../../../types/world/worldConstants'; import { useToolMode } from '../../../../../../store/builder/store'; @@ -8,7 +8,13 @@ import { useBuilderStore } from '../../../../../../store/builder/useBuilderStore function ArrowsAisle({ aisle }: { readonly aisle: Aisle }) { const aisleRef = useRef(null); const { toolMode } = useToolMode(); - const { setSelectedAisle, hoveredPoint } = useBuilderStore(); + const { setSelectedAisle, hoveredPoint, selectedAisle } = useBuilderStore(); + + useEffect(() => { + if (selectedAisle?.aisleData.aisleUuid === aisle.aisleUuid && !selectedAisle.aisleMesh) { + setSelectedAisle({ aisleData: selectedAisle.aisleData, aisleMesh: aisleRef.current }); + } + }, [selectedAisle]) const { arrowGeometry, arrowInstances } = useMemo(() => { const result = { @@ -68,8 +74,8 @@ function ArrowsAisle({ aisle }: { readonly aisle: Aisle }) { }, [aisle]); const handleClick = () => { - if (toolMode === 'move' && !hoveredPoint) { - setSelectedAisle(aisleRef.current); + if ((toolMode === 'move' || toolMode === 'cursor') && !hoveredPoint) { + setSelectedAisle({ aisleMesh: aisleRef.current, aisleData: aisle }); } }; diff --git a/app/src/modules/builder/aisle/Instances/instance/aisleTypes/circleAisle.tsx b/app/src/modules/builder/aisle/Instances/instance/aisleTypes/circleAisle.tsx index af852e8..d9bc2d2 100644 --- a/app/src/modules/builder/aisle/Instances/instance/aisleTypes/circleAisle.tsx +++ b/app/src/modules/builder/aisle/Instances/instance/aisleTypes/circleAisle.tsx @@ -1,5 +1,5 @@ import * as THREE from 'three'; -import { useMemo, useRef } from 'react'; +import { useEffect, useMemo, useRef } from 'react'; import { Extrude } from '@react-three/drei'; import * as Constants from '../../../../../../types/world/worldConstants'; import { useToolMode } from '../../../../../../store/builder/store'; @@ -8,7 +8,13 @@ import { useBuilderStore } from '../../../../../../store/builder/useBuilderStore function CircleAisle({ aisle }: { readonly aisle: Aisle }) { const aisleRef = useRef(null); const { toolMode } = useToolMode(); - const { setSelectedAisle, hoveredPoint } = useBuilderStore(); + const { setSelectedAisle, hoveredPoint, selectedAisle } = useBuilderStore(); + + useEffect(() => { + if (selectedAisle?.aisleData.aisleUuid === aisle.aisleUuid && !selectedAisle.aisleMesh) { + setSelectedAisle({ aisleData: selectedAisle.aisleData, aisleMesh: aisleRef.current }); + } + }, [selectedAisle]) const circle = useMemo(() => { if (aisle.points.length < 2 || aisle.type.aisleType !== 'circle-aisle') return null; @@ -38,8 +44,8 @@ function CircleAisle({ aisle }: { readonly aisle: Aisle }) { }, [aisle]); const handleClick = () => { - if (toolMode === 'move' && !hoveredPoint) { - setSelectedAisle(aisleRef.current); + if ((toolMode === 'move' || toolMode === 'cursor') && !hoveredPoint) { + setSelectedAisle({ aisleMesh: aisleRef.current, aisleData: aisle }); } } diff --git a/app/src/modules/builder/aisle/Instances/instance/aisleTypes/dashedAisle.tsx b/app/src/modules/builder/aisle/Instances/instance/aisleTypes/dashedAisle.tsx index 72fb3c7..b00c87b 100644 --- a/app/src/modules/builder/aisle/Instances/instance/aisleTypes/dashedAisle.tsx +++ b/app/src/modules/builder/aisle/Instances/instance/aisleTypes/dashedAisle.tsx @@ -1,5 +1,5 @@ import * as THREE from 'three'; -import { useMemo, useRef } from 'react'; +import { useEffect, useMemo, useRef } from 'react'; import { Instances, Instance } from '@react-three/drei'; import * as Constants from '../../../../../../types/world/worldConstants'; import { useToolMode } from '../../../../../../store/builder/store'; @@ -8,7 +8,13 @@ import { useBuilderStore } from '../../../../../../store/builder/useBuilderStore function DashedAisle({ aisle }: { readonly aisle: Aisle }) { const aisleRef = useRef(null); const { toolMode } = useToolMode(); - const { setSelectedAisle, hoveredPoint } = useBuilderStore(); + const { setSelectedAisle, hoveredPoint, selectedAisle } = useBuilderStore(); + + useEffect(() => { + if (selectedAisle?.aisleData.aisleUuid === aisle.aisleUuid && !selectedAisle.aisleMesh) { + setSelectedAisle({ aisleData: selectedAisle.aisleData, aisleMesh: aisleRef.current }); + } + }, [selectedAisle]) const dashInstances = useMemo(() => { if (aisle.points.length < 2 || aisle.type.aisleType !== 'dashed-aisle') return []; @@ -43,8 +49,8 @@ function DashedAisle({ aisle }: { readonly aisle: Aisle }) { }, [aisle]); const handleClick = () => { - if (toolMode === 'move' && !hoveredPoint) { - setSelectedAisle(aisleRef.current); + if ((toolMode === 'move' || toolMode === 'cursor') && !hoveredPoint) { + setSelectedAisle({ aisleMesh: aisleRef.current, aisleData: aisle }); } }; diff --git a/app/src/modules/builder/aisle/Instances/instance/aisleTypes/dottedAisle.tsx b/app/src/modules/builder/aisle/Instances/instance/aisleTypes/dottedAisle.tsx index e2b264a..09557aa 100644 --- a/app/src/modules/builder/aisle/Instances/instance/aisleTypes/dottedAisle.tsx +++ b/app/src/modules/builder/aisle/Instances/instance/aisleTypes/dottedAisle.tsx @@ -1,5 +1,5 @@ import * as THREE from 'three'; -import { useMemo, useRef } from 'react'; +import { useEffect, useMemo, useRef } from 'react'; import { Instance, Instances } from '@react-three/drei'; import * as Constants from '../../../../../../types/world/worldConstants'; import { useToolMode } from '../../../../../../store/builder/store'; @@ -8,7 +8,13 @@ import { useBuilderStore } from '../../../../../../store/builder/useBuilderStore function DottedAisle({ aisle }: { readonly aisle: Aisle }) { const aisleRef = useRef(null); const { toolMode } = useToolMode(); - const { setSelectedAisle, hoveredPoint } = useBuilderStore(); + const { setSelectedAisle, hoveredPoint, selectedAisle } = useBuilderStore(); + + useEffect(() => { + if (selectedAisle?.aisleData.aisleUuid === aisle.aisleUuid && !selectedAisle.aisleMesh) { + setSelectedAisle({ aisleData: selectedAisle.aisleData, aisleMesh: aisleRef.current }); + } + }, [selectedAisle]) const dotPositions = useMemo(() => { if (aisle.points.length < 2 || aisle.type.aisleType !== 'dotted-aisle') return []; @@ -27,8 +33,8 @@ function DottedAisle({ aisle }: { readonly aisle: Aisle }) { }, [aisle]); const handleClick = () => { - if (toolMode === 'move' && !hoveredPoint) { - setSelectedAisle(aisleRef.current); + if ((toolMode === 'move' || toolMode === 'cursor') && !hoveredPoint) { + setSelectedAisle({ aisleMesh: aisleRef.current, aisleData: aisle }); } } diff --git a/app/src/modules/builder/aisle/Instances/instance/aisleTypes/junctionAisle.tsx b/app/src/modules/builder/aisle/Instances/instance/aisleTypes/junctionAisle.tsx index a3f8bdf..8d45763 100644 --- a/app/src/modules/builder/aisle/Instances/instance/aisleTypes/junctionAisle.tsx +++ b/app/src/modules/builder/aisle/Instances/instance/aisleTypes/junctionAisle.tsx @@ -1,5 +1,5 @@ import * as THREE from 'three'; -import { useMemo, useRef } from 'react'; +import { useEffect, useMemo, useRef } from 'react'; import { Extrude } from '@react-three/drei'; import * as Constants from '../../../../../../types/world/worldConstants'; import { useToolMode } from '../../../../../../store/builder/store'; @@ -8,7 +8,13 @@ import { useBuilderStore } from '../../../../../../store/builder/useBuilderStore function JunctionAisle({ aisle }: { readonly aisle: Aisle }) { const aisleRef = useRef(null); const { toolMode } = useToolMode(); - const { setSelectedAisle, hoveredPoint } = useBuilderStore(); + const { setSelectedAisle, hoveredPoint, selectedAisle } = useBuilderStore(); + + useEffect(() => { + if (selectedAisle?.aisleData.aisleUuid === aisle.aisleUuid && !selectedAisle.aisleMesh) { + setSelectedAisle({ aisleData: selectedAisle.aisleData, aisleMesh: aisleRef.current }); + } + }, [selectedAisle]) const arrows = useMemo(() => { if (aisle.points.length < 2 || aisle.type.aisleType !== 'junction-aisle') return null; @@ -85,8 +91,8 @@ function JunctionAisle({ aisle }: { readonly aisle: Aisle }) { }, [aisle]); const handleClick = () => { - if (toolMode === 'move' && !hoveredPoint) { - setSelectedAisle(aisleRef.current); + if ((toolMode === 'move' || toolMode === 'cursor') && !hoveredPoint) { + setSelectedAisle({ aisleMesh: aisleRef.current, aisleData: aisle }); } } diff --git a/app/src/modules/builder/aisle/Instances/instance/aisleTypes/solidAisle.tsx b/app/src/modules/builder/aisle/Instances/instance/aisleTypes/solidAisle.tsx index e9321b0..e43f717 100644 --- a/app/src/modules/builder/aisle/Instances/instance/aisleTypes/solidAisle.tsx +++ b/app/src/modules/builder/aisle/Instances/instance/aisleTypes/solidAisle.tsx @@ -1,5 +1,5 @@ import * as THREE from 'three'; -import { useMemo, useRef } from 'react'; +import { useEffect, useMemo, useRef } from 'react'; import { Extrude } from '@react-three/drei'; import * as Constants from '../../../../../../types/world/worldConstants'; import { useToolMode } from '../../../../../../store/builder/store'; @@ -8,7 +8,14 @@ import { useBuilderStore } from '../../../../../../store/builder/useBuilderStore function SolidAisle({ aisle }: { readonly aisle: Aisle }) { const aisleRef = useRef(null); const { toolMode } = useToolMode(); - const { setSelectedAisle, hoveredPoint } = useBuilderStore(); + const { setSelectedAisle, hoveredPoint, selectedAisle } = useBuilderStore(); + + useEffect(() => { + if (selectedAisle?.aisleData.aisleUuid === aisle.aisleUuid && !selectedAisle.aisleMesh) { + setSelectedAisle({ aisleData: selectedAisle.aisleData, aisleMesh: aisleRef.current }); + } + }, [selectedAisle]) + const shape = useMemo(() => { if (aisle.points.length < 2 || aisle.type.aisleType !== 'solid-aisle') return null; @@ -35,8 +42,8 @@ function SolidAisle({ aisle }: { readonly aisle: Aisle }) { }, [aisle]); const handleClick = () => { - if (toolMode === 'move' && !hoveredPoint) { - setSelectedAisle(aisleRef.current); + if ((toolMode === 'move' || toolMode === 'cursor') && !hoveredPoint) { + setSelectedAisle({ aisleMesh: aisleRef.current, aisleData: aisle }); } } diff --git a/app/src/modules/scene/postProcessing/postProcessing.tsx b/app/src/modules/scene/postProcessing/postProcessing.tsx index 24f7428..e6d656d 100644 --- a/app/src/modules/scene/postProcessing/postProcessing.tsx +++ b/app/src/modules/scene/postProcessing/postProcessing.tsx @@ -120,7 +120,7 @@ export default function PostProcessing() { )} {selectedAisle && ( void; - addAisle: (aisle: Aisle) => void; - updateAisle: (uuid: string, updated: Partial) => void; - removeAisle: (uuid: string) => void; + setAisles: (aisles: Aisles) => Aisles; + addAisle: (aisle: Aisle) => Aisle; + updateAisle: (uuid: string, updated: Partial) => Aisle | undefined; + removeAisle: (uuid: string) => Aisle | undefined; removePoint: (uuid: string) => Aisles; - clearAisles: () => void; + clearAisles: () => Aisles; setPosition: ( pointUuid: string, position: [number, number, number] ) => Aisle | undefined; - setLayer: (pointUuid: string, layer: number) => void; - setColor: (aisleUuid: string, color: AisleColors) => void; + setLayer: (pointUuid: string, layer: number) => Aisle | undefined; + setColor: (aisleUuid: string, color: AisleColors) => Aisle | undefined; - // Type-specific setters - setSolidAisleWidth: (aisleUuid: string, width: number) => void; + setSolidAisleWidth: (aisleUuid: string, width: number) => Aisle | undefined; setDashedAisleProperties: ( aisleUuid: string, props: { aisleWidth?: number; dashLength?: number; gapLength?: number } - ) => void; + ) => Aisle | undefined; setDottedAisleProperties: ( aisleUuid: string, props: { dotRadius?: number; gapLength?: number } - ) => void; - setArrowAisleWidth: (aisleUuid: string, width: number) => void; + ) => Aisle | undefined; + setArrowAisleWidth: (aisleUuid: string, width: number) => Aisle | undefined; setArrowsAisleProperties: ( aisleUuid: string, props: { aisleWidth?: number; aisleLength?: number; gapLength?: number } - ) => void; + ) => Aisle | undefined; setArcAisleWidth: ( aisleUuid: string, props: { aisleWidth?: number; isFlipped: boolean } - ) => void; - setCircleAisleWidth: (aisleUuid: string, width: number) => void; + ) => Aisle | undefined; + setCircleAisleWidth: (aisleUuid: string, width: number) => Aisle | undefined; setJunctionAisleProperties: ( aisleUuid: string, props: { aisleWidth?: number; isFlipped: boolean } - ) => void; + ) => Aisle | undefined; getAisleById: (uuid: string) => Aisle | undefined; getAislesByPointId: (uuid: string) => Aisle[] | []; @@ -53,28 +52,43 @@ export const createAisleStore = () => { immer((set, get) => ({ aisles: [], - setAisles: (aisles) => + setAisles: (aisles) => { set((state) => { state.aisles = aisles; - }), + }); + return aisles; + }, - addAisle: (aisle) => + addAisle: (aisle) => { set((state) => { state.aisles.push(aisle); - }), + }); + return aisle; + }, - updateAisle: (uuid, updated) => + updateAisle: (uuid, updated) => { + let updatedAisle: Aisle | undefined; set((state) => { const aisle = state.aisles.find((a) => a.aisleUuid === uuid); if (aisle) { Object.assign(aisle, updated); + updatedAisle = JSON.parse(JSON.stringify(aisle)); } - }), + }); + return updatedAisle; + }, - removeAisle: (uuid) => + removeAisle: (uuid) => { + let removedAisle: Aisle | undefined; set((state) => { - state.aisles = state.aisles.filter((a) => a.aisleUuid !== uuid); - }), + const index = state.aisles.findIndex((a) => a.aisleUuid === uuid); + if (index !== -1) { + removedAisle = JSON.parse(JSON.stringify(state.aisles[index])); + state.aisles.splice(index, 1); + } + }); + return removedAisle; + }, removePoint: (uuid) => { const removedAisles: Aisle[] = []; @@ -94,9 +108,11 @@ export const createAisleStore = () => { }, clearAisles: () => { + const clearedAisles = get().aisles; set((state) => { state.aisles = []; - }) + }); + return clearedAisles; }, setPosition: (pointUuid, position) => { @@ -113,34 +129,46 @@ export const createAisleStore = () => { return updatedAisle; }, - setLayer: (pointUuid, layer) => + setLayer: (pointUuid, layer) => { + let updatedAisle: Aisle | undefined; set((state) => { for (const aisle of state.aisles) { const point = aisle.points.find((p) => p.pointUuid === pointUuid); if (point) { point.layer = layer; + updatedAisle = JSON.parse(JSON.stringify(aisle)); } } - }), + }); + return updatedAisle; + }, - setColor: (aisleUuid, color) => + setColor: (aisleUuid, color) => { + let updatedAisle: Aisle | undefined; set((state) => { const aisle = state.aisles.find((a) => a.aisleUuid === aisleUuid); if (aisle) { aisle.type.aisleColor = color; + updatedAisle = JSON.parse(JSON.stringify(aisle)); } - }), + }); + return updatedAisle; + }, - // Type-specific property setters - setSolidAisleWidth: (aisleUuid, width) => + setSolidAisleWidth: (aisleUuid, width) => { + let updatedAisle: Aisle | undefined; set((state) => { const aisle = state.aisles.find((a) => a.aisleUuid === aisleUuid); if (aisle && aisle.type.aisleType === "solid-aisle") { aisle.type.aisleWidth = width; + updatedAisle = JSON.parse(JSON.stringify(aisle)); } - }), + }); + return updatedAisle; + }, - setDashedAisleProperties: (aisleUuid, props) => + setDashedAisleProperties: (aisleUuid, props) => { + let updatedAisle: Aisle | undefined; set((state) => { const aisle = state.aisles.find((a) => a.aisleUuid === aisleUuid); if (aisle && aisle.type.aisleType === "dashed-aisle") { @@ -150,10 +178,14 @@ export const createAisleStore = () => { aisle.type.dashLength = props.dashLength; if (props.gapLength !== undefined) aisle.type.gapLength = props.gapLength; + updatedAisle = JSON.parse(JSON.stringify(aisle)); } - }), + }); + return updatedAisle; + }, - setDottedAisleProperties: (aisleUuid, props) => + setDottedAisleProperties: (aisleUuid, props) => { + let updatedAisle: Aisle | undefined; set((state) => { const aisle = state.aisles.find((a) => a.aisleUuid === aisleUuid); if (aisle && aisle.type.aisleType === "dotted-aisle") { @@ -161,18 +193,26 @@ export const createAisleStore = () => { aisle.type.dotRadius = props.dotRadius; if (props.gapLength !== undefined) aisle.type.gapLength = props.gapLength; + updatedAisle = JSON.parse(JSON.stringify(aisle)); } - }), + }); + return updatedAisle; + }, - setArrowAisleWidth: (aisleUuid, width) => + setArrowAisleWidth: (aisleUuid, width) => { + let updatedAisle: Aisle | undefined; set((state) => { const aisle = state.aisles.find((a) => a.aisleUuid === aisleUuid); if (aisle && aisle.type.aisleType === "arrow-aisle") { aisle.type.aisleWidth = width; + updatedAisle = JSON.parse(JSON.stringify(aisle)); } - }), + }); + return updatedAisle; + }, - setArrowsAisleProperties: (aisleUuid, props) => + setArrowsAisleProperties: (aisleUuid, props) => { + let updatedAisle: Aisle | undefined; set((state) => { const aisle = state.aisles.find((a) => a.aisleUuid === aisleUuid); if (aisle && aisle.type.aisleType === "arrows-aisle") { @@ -182,10 +222,14 @@ export const createAisleStore = () => { aisle.type.aisleLength = props.aisleLength; if (props.gapLength !== undefined) aisle.type.gapLength = props.gapLength; + updatedAisle = JSON.parse(JSON.stringify(aisle)); } - }), + }); + return updatedAisle; + }, - setArcAisleWidth: (aisleUuid, props) => + setArcAisleWidth: (aisleUuid, props) => { + let updatedAisle: Aisle | undefined; set((state) => { const aisle = state.aisles.find((a) => a.aisleUuid === aisleUuid); if (aisle && aisle.type.aisleType === "arc-aisle") { @@ -193,18 +237,26 @@ export const createAisleStore = () => { aisle.type.aisleWidth = props.aisleWidth; if (props.isFlipped !== undefined) aisle.type.isFlipped = props.isFlipped; + updatedAisle = JSON.parse(JSON.stringify(aisle)); } - }), + }); + return updatedAisle; + }, - setCircleAisleWidth: (aisleUuid, width) => + setCircleAisleWidth: (aisleUuid, width) => { + let updatedAisle: Aisle | undefined; set((state) => { const aisle = state.aisles.find((a) => a.aisleUuid === aisleUuid); if (aisle && aisle.type.aisleType === "circle-aisle") { aisle.type.aisleWidth = width; + updatedAisle = JSON.parse(JSON.stringify(aisle)); } - }), + }); + return updatedAisle; + }, - setJunctionAisleProperties: (aisleUuid, props) => + setJunctionAisleProperties: (aisleUuid, props) => { + let updatedAisle: Aisle | undefined; set((state) => { const aisle = state.aisles.find((a) => a.aisleUuid === aisleUuid); if (aisle && aisle.type.aisleType === "junction-aisle") { @@ -212,8 +264,11 @@ export const createAisleStore = () => { aisle.type.aisleWidth = props.aisleWidth; if (props.isFlipped !== undefined) aisle.type.isFlipped = props.isFlipped; + updatedAisle = JSON.parse(JSON.stringify(aisle)); } - }), + }); + return updatedAisle; + }, getAisleById: (uuid) => { return get().aisles.find((a) => a.aisleUuid === uuid); diff --git a/app/src/store/builder/useBuilderStore.ts b/app/src/store/builder/useBuilderStore.ts index 476631d..376689b 100644 --- a/app/src/store/builder/useBuilderStore.ts +++ b/app/src/store/builder/useBuilderStore.ts @@ -47,7 +47,7 @@ interface BuilderState { }, // Aisle General - selectedAisle: Object3D | null; + selectedAisle: {aisleMesh: Object3D | null, aisleData: Aisle} | null; aisleType: AisleTypes; aisleWidth: number; aisleColor: AisleColors; @@ -97,7 +97,7 @@ interface BuilderState { setDecalDragState: (isDragging: boolean, draggingDecalUuid: string | null, dragOffset: Vector3 | null) => void; // Setters - Aisle General - setSelectedAisle: (aisle: Object3D | null) => void; + setSelectedAisle: (aisle: {aisleMesh: Object3D | null, aisleData: Aisle} | null) => void; setAisleType: (type: AisleTypes) => void; setAisleWidth: (width: number) => void; setAisleColor: (color: AisleColors) => void; @@ -325,7 +325,7 @@ export const useBuilderStore = create()( // === Setters: Aisle General === - setSelectedAisle: (aisle: Object3D | null) => { + setSelectedAisle: (aisle: {aisleMesh: Object3D | null, aisleData: Aisle} | null) => { set((state) => { state.selectedAisle = aisle; }); From 7bbb221c663f4f5cfab16aabbbd2cc305d821dd2 Mon Sep 17 00:00:00 2001 From: Vishnu Date: Thu, 28 Aug 2025 18:06:34 +0530 Subject: [PATCH 21/34] - asset icons updated - aisle props changes - aisle style updated --- app/src/components/icons/AssetTypeIcons.tsx | 353 ++++++++++++++++++ .../components/icons/ExportCommonIcons.tsx | 87 ----- .../components/layout/sidebarLeft/Assets.tsx | 4 +- .../properties/AisleProperties.tsx | 21 +- .../hrm/assetManagement/AssetManagement.tsx | 15 +- app/src/components/ui/Tools.tsx | 2 +- app/src/store/builder/useBuilderStore.ts | 2 +- app/src/styles/layout/sidebar.scss | 24 +- app/src/types/builderTypes.d.ts | 2 +- 9 files changed, 400 insertions(+), 110 deletions(-) create mode 100644 app/src/components/icons/AssetTypeIcons.tsx diff --git a/app/src/components/icons/AssetTypeIcons.tsx b/app/src/components/icons/AssetTypeIcons.tsx new file mode 100644 index 0000000..2f32358 --- /dev/null +++ b/app/src/components/icons/AssetTypeIcons.tsx @@ -0,0 +1,353 @@ +export const ForkLiftIcon = () => { + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + ); +}; + +export const ConveyorIcon = () => { + return ( + + + + + + + + + + + + + + + ); +}; + +export const RoboticArmIcon = () => { + return ( + + + + + + + + + + + + + + + + + ); +}; + +export const MachineIcon = () => { + return ( + + + + + + + + + + + + + + + + + + + + + + + + ); +}; + +type TypeBasedAssetIconsProps = { + assetType: string; +}; + +export function TypeBasedAssetIcons({ assetType }: TypeBasedAssetIconsProps) { + console.log("assetType: ", assetType); + return ( +
+ {assetType === "machine" && } + {assetType === "vehicle" && } + {assetType === "transfer" && } + {assetType === "roboticArm" && } +
+ ); +} diff --git a/app/src/components/icons/ExportCommonIcons.tsx b/app/src/components/icons/ExportCommonIcons.tsx index 72f9ca0..eaf1cf4 100644 --- a/app/src/components/icons/ExportCommonIcons.tsx +++ b/app/src/components/icons/ExportCommonIcons.tsx @@ -1697,93 +1697,6 @@ export const TargetIcon = () => { ); }; -export const ForkLiftIcon = () => { - return ( - - - - - - - - - - - - - - - - - - - - - - - - - - - ); -}; export const RightHalfFillCircleIcon = () => { return ( {

- Results for '{searchValue}' + Results for{" "} + '{searchValue}'

@@ -256,7 +257,6 @@ const Assets: React.FC = () => {
)} - {selectedCategory !== "Decals" && !selectedSubCategory ? (
{categoryAssets?.map((asset: any, index: number) => ( diff --git a/app/src/components/layout/sidebarRight/properties/AisleProperties.tsx b/app/src/components/layout/sidebarRight/properties/AisleProperties.tsx index c343805..2fb8042 100644 --- a/app/src/components/layout/sidebarRight/properties/AisleProperties.tsx +++ b/app/src/components/layout/sidebarRight/properties/AisleProperties.tsx @@ -28,13 +28,13 @@ const AisleProperties: React.FC = () => { const { aisleType, aisleWidth, aisleColor, dashLength, gapLength, dotRadius, aisleLength, isFlipped, setAisleType, setAisleColor, setAisleWidth, setDashLength, setGapLength, setDotRadius, setAisleLength, setIsFlipped } = useBuilderStore(); const aisleTextureList: TextureItem[] = [ - { color: "yellow", id: "yellow", brief: "pedestrian walkways", texture: "" }, - { color: "gray", id: "gray", brief: "basic", texture: "" }, - { color: "green", id: "green", brief: "pedestrian walkways", texture: "" }, - { color: "orange", id: "orange", brief: "material flow", texture: "" }, - { color: "blue", id: "blue", brief: "vehicle paths", texture: "" }, - { color: "purple", id: "purple", brief: "material flow", texture: "" }, - { color: "red", id: "red", brief: "safety zone", texture: "" }, + { color: "yellow", id: "#FBE50E", brief: "pedestrian walkways", texture: "" }, + { color: "gray", id: "#6F6F7A", brief: "basic", texture: "" }, + { color: "green", id: "#43C06D", brief: "pedestrian walkways", texture: "" }, + { color: "orange", id: "#FF711B", brief: "material flow", texture: "" }, + { color: "blue", id: "#488EF6", brief: "vehicle paths", texture: "" }, + { color: "purple", id: "#AF52DE", brief: "material flow", texture: "" }, + { color: "red", id: "#FF3B30", brief: "safety zone", texture: "" }, { color: "bright green", id: "#66FF00", brief: "safety zone", texture: "" }, { color: "yellow-black", id: "yellow-black", brief: "utility aisles", texture: "" }, { color: "white-black", id: "white-black", brief: "utility aisles", texture: "" }, @@ -285,7 +285,12 @@ const AisleProperties: React.FC = () => { onClick={() => setAisleColor(val.id)} aria-pressed={aisleColor === val.id} > -
{val.texture}
+
+ {val.texture} +
{val.color}
{`( ${val.brief} )`}
diff --git a/app/src/components/layout/sidebarRight/resourceManagement/hrm/assetManagement/AssetManagement.tsx b/app/src/components/layout/sidebarRight/resourceManagement/hrm/assetManagement/AssetManagement.tsx index faa6015..718915c 100644 --- a/app/src/components/layout/sidebarRight/resourceManagement/hrm/assetManagement/AssetManagement.tsx +++ b/app/src/components/layout/sidebarRight/resourceManagement/hrm/assetManagement/AssetManagement.tsx @@ -1,18 +1,19 @@ import { useEffect, useState } from 'react' // import NavigateCatagory from '../../NavigateCatagory' -import { EyeIcon, ForkLiftIcon, KebabIcon, LocationPinIcon, RightHalfFillCircleIcon } from '../../../../../icons/ExportCommonIcons'; +import { EyeIcon, KebabIcon, LocationPinIcon, RightHalfFillCircleIcon } from '../../../../../icons/ExportCommonIcons'; import assetImage from "../../../../../../assets/image/asset-image.png" import { useSceneContext } from '../../../../../../modules/scene/sceneContext'; import { useProductContext } from '../../../../../../modules/simulation/products/productContext'; import RenameInput from '../../../../../ui/inputs/RenameInput'; import { useResourceManagementId } from '../../../../../../store/builder/store'; +import { TypeBasedAssetIcons } from '../../../../../icons/AssetTypeIcons'; const AssetManagement = () => { // const [selectedCategory, setSelectedCategory] = useState("All Assets"); const [expandedAssetId, setExpandedAssetId] = useState(null); const [assets, setAssets] = useState([]); const { productStore } = useSceneContext(); - const { products, getProductById } = productStore(); + const { getProductById } = productStore(); const { selectedProductStore } = useProductContext(); const { selectedProduct } = selectedProductStore(); const { setResourceManagementId } = useResourceManagementId(); @@ -30,6 +31,7 @@ const AssetManagement = () => { grouped[asset.modelName] = { id: asset.modelUuid, name: asset.modelName, + type: asset.type, model: asset.modelCode || "N/A", status: asset.status || "Online", usageRate: asset.usageRate || 15, @@ -46,6 +48,7 @@ const AssetManagement = () => { setAssets(Object.values(grouped)); } + // eslint-disable-next-line }, [selectedProduct]); function handleRenameAsset(newName: string) { @@ -130,7 +133,7 @@ const AssetManagement = () => { : -
+
}
@@ -210,16 +213,10 @@ const AssetManagement = () => {
{expandedAssetId === asset.id ? "View Less" : "View More"}
-
- -
- )} -
- ))}
diff --git a/app/src/components/ui/Tools.tsx b/app/src/components/ui/Tools.tsx index b89f2ea..4d29ad0 100644 --- a/app/src/components/ui/Tools.tsx +++ b/app/src/components/ui/Tools.tsx @@ -374,7 +374,7 @@ const Tools: React.FC = () => { )}
- {activeModule !== "visualization" && ( + {toggleThreeD && activeModule !== "visualization" && ( <>
diff --git a/app/src/store/builder/useBuilderStore.ts b/app/src/store/builder/useBuilderStore.ts index 476631d..efda83a 100644 --- a/app/src/store/builder/useBuilderStore.ts +++ b/app/src/store/builder/useBuilderStore.ts @@ -158,7 +158,7 @@ export const useBuilderStore = create()( selectedAisle: null, aisleType: 'solid-aisle', aisleWidth: 0.1, - aisleColor: 'yellow', + aisleColor: '#FBE50E', dashLength: 0.5, gapLength: 0.3, diff --git a/app/src/styles/layout/sidebar.scss b/app/src/styles/layout/sidebar.scss index e3dd706..34f5b6d 100644 --- a/app/src/styles/layout/sidebar.scss +++ b/app/src/styles/layout/sidebar.scss @@ -1500,6 +1500,28 @@ border-radius: #{$border-radius-large}; margin-right: 4px; overflow: hidden; + &.yellow-black { + background-color: black; + background-size: 10px 10px; + background-image: repeating-linear-gradient( + 45deg, + #FBE50E 0, + #FBE50E 2px, + black 0, + black 50% + ); + } + &.white-black { + background-color: black; + background-size: 10px 10px; + background-image: repeating-linear-gradient( + 45deg, + white 0, + white 2px, + black 0, + black 50% + ); + } } .aisle-color { @@ -1997,7 +2019,7 @@ background: var(--background-color); border-radius: 0 0 12px 12px; backdrop-filter: blur(4px); - .search-for{ + .search-for { display: inline-block; color: var(--accent-color); max-width: 238px; diff --git a/app/src/types/builderTypes.d.ts b/app/src/types/builderTypes.d.ts index e0440ec..7d5eefb 100644 --- a/app/src/types/builderTypes.d.ts +++ b/app/src/types/builderTypes.d.ts @@ -151,7 +151,7 @@ type Zones = Zone[]; type AisleTypes = 'solid-aisle' | 'dashed-aisle' | 'stripped-aisle' | 'dotted-aisle' | 'arrow-aisle' | 'arrows-aisle' | 'arc-aisle' | 'circle-aisle' | 'junction-aisle'; -type AisleColors = 'gray' | 'yellow' | 'green' | 'orange' | 'blue' | 'purple' | 'red' | '#66FF00' | 'yellow-black' | 'white-black' +type AisleColors = '#6F6F7A' | '#FBE50E' | '#43C06D' | '#FF711B' | '#488EF6' | '#AF52DE' | '#FF3B30' | '#66FF00' | 'yellow-black' | 'white-black' interface SolidAisle { aisleType: 'solid-aisle'; From 982a8ef4aa2fd8e0befdf4b209b8779f2cd782a0 Mon Sep 17 00:00:00 2001 From: Vishnu Date: Thu, 28 Aug 2025 18:11:54 +0530 Subject: [PATCH 22/34] aisle props update --- .../properties/AisleProperties.tsx | 25 ++++++++-------- .../properties/SelectedAisleProperties.tsx | 30 +++++-------------- 2 files changed, 20 insertions(+), 35 deletions(-) diff --git a/app/src/components/layout/sidebarRight/properties/AisleProperties.tsx b/app/src/components/layout/sidebarRight/properties/AisleProperties.tsx index 2fb8042..ca2317d 100644 --- a/app/src/components/layout/sidebarRight/properties/AisleProperties.tsx +++ b/app/src/components/layout/sidebarRight/properties/AisleProperties.tsx @@ -21,24 +21,25 @@ interface TextureItem { texture: string; } +export const aisleTextureList: TextureItem[] = [ + { color: "yellow", id: "#FBE50E", brief: "pedestrian walkways", texture: "" }, + { color: "gray", id: "#6F6F7A", brief: "basic", texture: "" }, + { color: "green", id: "#43C06D", brief: "pedestrian walkways", texture: "" }, + { color: "orange", id: "#FF711B", brief: "material flow", texture: "" }, + { color: "blue", id: "#488EF6", brief: "vehicle paths", texture: "" }, + { color: "purple", id: "#AF52DE", brief: "material flow", texture: "" }, + { color: "red", id: "#FF3B30", brief: "safety zone", texture: "" }, + { color: "bright green", id: "#66FF00", brief: "safety zone", texture: "" }, + { color: "yellow-black", id: "yellow-black", brief: "utility aisles", texture: "" }, + { color: "white-black", id: "white-black", brief: "utility aisles", texture: "" }, +]; + const AisleProperties: React.FC = () => { const [collapsePresets, setCollapsePresets] = useState(false); const [collapseTexture, setCollapseTexture] = useState(true); const { aisleType, aisleWidth, aisleColor, dashLength, gapLength, dotRadius, aisleLength, isFlipped, setAisleType, setAisleColor, setAisleWidth, setDashLength, setGapLength, setDotRadius, setAisleLength, setIsFlipped } = useBuilderStore(); - const aisleTextureList: TextureItem[] = [ - { color: "yellow", id: "#FBE50E", brief: "pedestrian walkways", texture: "" }, - { color: "gray", id: "#6F6F7A", brief: "basic", texture: "" }, - { color: "green", id: "#43C06D", brief: "pedestrian walkways", texture: "" }, - { color: "orange", id: "#FF711B", brief: "material flow", texture: "" }, - { color: "blue", id: "#488EF6", brief: "vehicle paths", texture: "" }, - { color: "purple", id: "#AF52DE", brief: "material flow", texture: "" }, - { color: "red", id: "#FF3B30", brief: "safety zone", texture: "" }, - { color: "bright green", id: "#66FF00", brief: "safety zone", texture: "" }, - { color: "yellow-black", id: "yellow-black", brief: "utility aisles", texture: "" }, - { color: "white-black", id: "white-black", brief: "utility aisles", texture: "" }, - ]; const aisleTypes: { name: string; diff --git a/app/src/components/layout/sidebarRight/properties/SelectedAisleProperties.tsx b/app/src/components/layout/sidebarRight/properties/SelectedAisleProperties.tsx index c1e1d24..3dc2540 100644 --- a/app/src/components/layout/sidebarRight/properties/SelectedAisleProperties.tsx +++ b/app/src/components/layout/sidebarRight/properties/SelectedAisleProperties.tsx @@ -18,15 +18,7 @@ import { useSceneContext } from "../../../../modules/scene/sceneContext"; import { useVersionContext } from "../../../../modules/builder/version/versionContext"; import { useSocketStore } from "../../../../store/builder/store"; import { getUserData } from "../../../../functions/getUserData"; - -// import { upsertAisleApi } from "../../../../services/factoryBuilder/aisle/upsertAisleApi"; - -interface TextureItem { - color: string; - id: AisleColors; - brief: string; - texture: string; -} +import { aisleTextureList } from "./AisleProperties"; const SelectedAisleProperties: React.FC = () => { const [collapsePresets, setCollapsePresets] = useState(false); @@ -72,19 +64,6 @@ const SelectedAisleProperties: React.FC = () => { } } - const aisleTextureList: TextureItem[] = [ - { color: "yellow", id: "yellow", brief: "pedestrian walkways", texture: "" }, - { color: "gray", id: "gray", brief: "basic", texture: "" }, - { color: "green", id: "green", brief: "pedestrian walkways", texture: "" }, - { color: "orange", id: "orange", brief: "material flow", texture: "" }, - { color: "blue", id: "blue", brief: "vehicle paths", texture: "" }, - { color: "purple", id: "purple", brief: "material flow", texture: "" }, - { color: "red", id: "red", brief: "safety zone", texture: "" }, - { color: "bright green", id: "#66FF00", brief: "safety zone", texture: "" }, - { color: "yellow-black", id: "yellow-black", brief: "utility aisles", texture: "" }, - { color: "white-black", id: "white-black", brief: "utility aisles", texture: "" }, - ]; - const aisleTypes: { name: string; type: AisleTypes; @@ -530,7 +509,12 @@ const SelectedAisleProperties: React.FC = () => { onClick={() => handleColorChange(val.id)} aria-pressed={selectedAisleData.type.aisleColor === val.id} > -
{val.texture}
+
+ {val.texture} +
{val.color}
{`( ${val.brief} )`}
From 2ba418ab6c80c5b62646ec75e68b6637cd323523 Mon Sep 17 00:00:00 2001 From: Vishnu Date: Thu, 28 Aug 2025 18:15:10 +0530 Subject: [PATCH 23/34] dashboard style fix --- app/src/styles/pages/dashboard.scss | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/styles/pages/dashboard.scss b/app/src/styles/pages/dashboard.scss index 5a628a4..01024d0 100644 --- a/app/src/styles/pages/dashboard.scss +++ b/app/src/styles/pages/dashboard.scss @@ -144,7 +144,8 @@ } .cards-container { - height: 100%; + height: auto; + max-height: 100%; display: flex; flex-wrap: wrap; position: relative; From e23e339ed39359b9c14032d0bcc454abca9f63ea Mon Sep 17 00:00:00 2001 From: Vishnu Date: Fri, 29 Aug 2025 10:10:54 +0530 Subject: [PATCH 24/34] decal scale fix --- .../layout/sidebarRight/properties/SelectedDecalProperties.tsx | 2 +- app/src/modules/builder/Decal/decalCreator/decalCreator.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/components/layout/sidebarRight/properties/SelectedDecalProperties.tsx b/app/src/components/layout/sidebarRight/properties/SelectedDecalProperties.tsx index e605363..3a67865 100644 --- a/app/src/components/layout/sidebarRight/properties/SelectedDecalProperties.tsx +++ b/app/src/components/layout/sidebarRight/properties/SelectedDecalProperties.tsx @@ -146,7 +146,7 @@ const SelectedDecalProperties = () => { label="Scale" value={selectedDecal.decalData.decalScale || 1} min={0.1} - max={5} + max={2} step={0.1} onChange={(value: number) => handleScaleChange(value)} /> diff --git a/app/src/modules/builder/Decal/decalCreator/decalCreator.tsx b/app/src/modules/builder/Decal/decalCreator/decalCreator.tsx index 35b54ce..fe14974 100644 --- a/app/src/modules/builder/Decal/decalCreator/decalCreator.tsx +++ b/app/src/modules/builder/Decal/decalCreator/decalCreator.tsx @@ -99,7 +99,7 @@ function DecalCreator() { decalPosition: [point.x, point.y, -0.001], decalRotation: 0, decalOpacity: 1, - decalScale: 1, + decalScale: 0.5, } addDecalOnFloor(floorIntersect.object.userData.floorUuid, decal); From 363906aa120b1fc3b8f7cde9f3442288f04c9e1f Mon Sep 17 00:00:00 2001 From: Poovizhi Date: Fri, 29 Aug 2025 12:46:34 +0530 Subject: [PATCH 25/34] added search functionality for decals --- .../components/layout/sidebarLeft/Assets.tsx | 200 +++++++++++++----- .../vehicle/instances/vehicleInstances.tsx | 26 +-- 2 files changed, 156 insertions(+), 70 deletions(-) diff --git a/app/src/components/layout/sidebarLeft/Assets.tsx b/app/src/components/layout/sidebarLeft/Assets.tsx index 4e946d3..ff1788f 100644 --- a/app/src/components/layout/sidebarLeft/Assets.tsx +++ b/app/src/components/layout/sidebarLeft/Assets.tsx @@ -49,39 +49,54 @@ const Assets: React.FC = () => { const [searchValue, setSearchValue] = useState(""); const [selectedCategory, setSelectedCategory] = useState(null); const [categoryAssets, setCategoryAssets] = useState([]); - - const [filtereredAssets, setFiltereredAssets] = useState([]); + const [decalAsset, setDecalAsset] = useState(); + const [filtereredAssets, setFiltereredAssets] = useState( + [] + ); const [categoryList, setCategoryList] = useState([]); const [isLoading, setisLoading] = useState(false); // Loading state for assets + const { selectedSubCategory, setSelectedSubCategory } = useDecalStore(); const handleSearchChange = (value: string) => { - const searchTerm = value.toLowerCase(); + const searchTerm = searchValue + ? searchValue.toLowerCase() + : value.toLowerCase(); setSearchValue(value); + if (searchTerm.trim() === "" && !selectedCategory) { setCategoryAssets([]); + return; } - const filteredModels = filtereredAssets?.filter((model) => { - if (!model?.tags || !model?.filename || !model?.category) return false; - if (searchTerm.startsWith(":") && searchTerm.length > 1) { - const tagSearchTerm = searchTerm.slice(1); - return model.tags.toLowerCase().includes(tagSearchTerm); - } else if (selectedCategory) { - return ( - model.category - .toLowerCase() - .includes(selectedCategory.toLowerCase()) && - model.filename.toLowerCase().includes(searchTerm) - ); - } else { - return model.filename.toLowerCase().includes(searchTerm); - } - }); + if (selectedCategory === "Decals" || selectedSubCategory) { + const filteredModels = decalAsset?.filter((model: any) => + model.decalName?.toLowerCase().includes(searchTerm.toLowerCase()) + ); + setCategoryAssets(filteredModels); + } else { + const filteredModels = filtereredAssets?.filter((model) => { + if (!model?.tags || !model?.filename || !model?.category) return false; + if (searchTerm.startsWith(":") && searchTerm.length > 1) { + const tagSearchTerm = searchTerm.slice(1); + return model.tags.toLowerCase().includes(tagSearchTerm); + } else if (selectedCategory) { + return ( + model.category + .toLowerCase() + .includes(selectedCategory.toLowerCase()) && + model.filename.toLowerCase().includes(searchTerm) + ); + } else { + return model.filename.toLowerCase().includes(searchTerm); + } + }); - setCategoryAssets(filteredModels); + setCategoryAssets(filteredModels); + } }; useEffect(() => { + if (selectedCategory === "Decals") return; const filteredAssets = async () => { try { const filt = await fetchAssets(); @@ -91,7 +106,18 @@ const Assets: React.FC = () => { } }; filteredAssets(); - }, [categoryAssets]); + }, [categoryAssets, selectedCategory]); + useEffect(() => { + if ( + (searchValue.trim() === "" && selectedCategory === "Decals") || + selectedSubCategory + ) { + const filteredModels = decalAsset?.filter((model: any) => + model.decalName?.toLowerCase().includes(searchValue.toLowerCase()) + ); + setCategoryAssets(filteredModels); + } + }, [selectedSubCategory, decalAsset, searchValue]); useEffect(() => { setCategoryList([ @@ -126,8 +152,10 @@ const Assets: React.FC = () => { // setSelectedCategory(asset); try { const res = await getCategoryDecals(asset); + setCategoryAssets(res); setFiltereredAssets(res); + setDecalAsset(res); setisLoading(false); // End loading // eslint-disable-next-line } catch (error) { @@ -143,11 +171,9 @@ const Assets: React.FC = () => { { name: "Informational", icon: }, ]; - const { selectedSubCategory, setSelectedSubCategory } = useDecalStore(); - return (
- +
{(() => { @@ -162,40 +188,100 @@ const Assets: React.FC = () => {

Results for {searchValue}

- {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(" ")} + {selectedCategory == "Decals" ? ( + <> +
+ {activeSubcategories.map((cat, index) => ( +
{ + fetchCategoryDecals(cat.name); + setSelectedSubCategory(cat.name); + }} + > +
{cat.icon}
+
{cat.name}
+
+ ))}
-
- ))} + {categoryAssets?.map((asset: any, index: number) => ( +
+ {asset.decalName} { + setSelectedItem({ + name: asset.decalName, + id: asset.id, + type: + asset.type === "undefined" + ? undefined + : asset.type, + category: asset.category, + // subType: asset.subType, + }); + }} + /> +
+ {asset.decalName + .split("_") + .map( + (word: any) => + word.charAt(0).toUpperCase() + + word.slice(1) + ) + .join(" ")} +
+
+ ))} + + ) : ( + 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(" ")} +
+
+ )) + )}
diff --git a/app/src/modules/simulation/vehicle/instances/vehicleInstances.tsx b/app/src/modules/simulation/vehicle/instances/vehicleInstances.tsx index 8f2b3f3..989242b 100644 --- a/app/src/modules/simulation/vehicle/instances/vehicleInstances.tsx +++ b/app/src/modules/simulation/vehicle/instances/vehicleInstances.tsx @@ -5,20 +5,20 @@ import { useSceneContext } from "../../../scene/sceneContext"; import { useViewSceneStore } from "../../../../store/builder/store"; function VehicleInstances() { - const { vehicleStore } = useSceneContext(); - const { vehicles } = vehicleStore(); - const { viewSceneLabels } = useViewSceneStore(); + const { vehicleStore } = useSceneContext(); + const { vehicles } = vehicleStore(); + const { viewSceneLabels } = useViewSceneStore(); - return ( - <> - {vehicles.map((vehicle: VehicleStatus) => ( - - - {viewSceneLabels && } - - ))} - - ); + return ( + <> + {vehicles.map((vehicle: VehicleStatus) => ( + + + {viewSceneLabels && } + + ))} + + ); } export default VehicleInstances; From b6783f99d35d571d6ea0b96d9287e94fa37b871f Mon Sep 17 00:00:00 2001 From: Vishnu Date: Fri, 29 Aug 2025 13:47:07 +0530 Subject: [PATCH 26/34] refactor: reorganize asset management components and enhance search functionality --- .../components/layout/sidebarLeft/Assets.tsx | 474 ------------------ .../layout/sidebarLeft/SideBarLeft.tsx | 2 +- .../layout/sidebarLeft/assetList/Assets.tsx | 177 +++++++ .../assetList/assetsHelpers/constants.tsx | 34 ++ .../assetsHelpers/fetchAssetsHelper.ts | 22 + .../assetsHelpers/fetchDecalsHelper.ts | 11 + .../assetsHelpers/filteredAssetsHelper.ts | 34 ++ .../assetsHelpers/renderAssetHelper.tsx | 58 +++ app/src/components/ui/inputs/Search.tsx | 14 +- .../cameraShortcutsControls.tsx | 7 + app/src/types/uiTypes.d.ts | 29 ++ 11 files changed, 384 insertions(+), 478 deletions(-) delete mode 100644 app/src/components/layout/sidebarLeft/Assets.tsx create mode 100644 app/src/components/layout/sidebarLeft/assetList/Assets.tsx create mode 100644 app/src/components/layout/sidebarLeft/assetList/assetsHelpers/constants.tsx create mode 100644 app/src/components/layout/sidebarLeft/assetList/assetsHelpers/fetchAssetsHelper.ts create mode 100644 app/src/components/layout/sidebarLeft/assetList/assetsHelpers/fetchDecalsHelper.ts create mode 100644 app/src/components/layout/sidebarLeft/assetList/assetsHelpers/filteredAssetsHelper.ts create mode 100644 app/src/components/layout/sidebarLeft/assetList/assetsHelpers/renderAssetHelper.tsx create mode 100644 app/src/types/uiTypes.d.ts diff --git a/app/src/components/layout/sidebarLeft/Assets.tsx b/app/src/components/layout/sidebarLeft/Assets.tsx deleted file mode 100644 index fbfe00b..0000000 --- a/app/src/components/layout/sidebarLeft/Assets.tsx +++ /dev/null @@ -1,474 +0,0 @@ -import React, { useEffect, useState } from "react"; -import Search from "../../ui/inputs/Search"; -import { getCategoryAsset } from "../../../services/factoryBuilder/asset/assets/getCategoryAsset"; -import { fetchAssets } from "../../../services/marketplace/fetchAssets"; -import { - useDecalStore, - useDroppedDecal, - useSelectedItem, -} from "../../../store/builder/store"; - -// images ------------------- -import vehicle from "../../../assets/image/categories/vehicles.png"; -import workStation from "../../../assets/image/categories/workStation.png"; -import machines from "../../../assets/image/categories/machines.png"; -import worker from "../../../assets/image/categories/worker.png"; -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 decal from "../../../assets/image/categories/decal.png"; -import SkeletonUI from "../../templates/SkeletonUI"; -import { - AlertIcon, - ArrowIcon, - DecalInfoIcon, - HangTagIcon, - NavigationIcon, -} from "../../icons/ExportCommonIcons"; -import { getCategoryDecals } from "../../../services/factoryBuilder/asset/decals/getCategoryDecals"; -// ------------------------------------- - -interface AssetProp { - filename: string; - thumbnail?: string; - category: string; - description?: string; - tags: string; - url?: string; - uploadDate?: number; - isArchieve?: boolean; - animated?: boolean; - price?: number; - CreatedBy?: string; -} -interface CategoryListProp { - assetImage?: string; - assetName?: string; - categoryImage: string; - category: string; -} -const Assets: React.FC = () => { - const { setSelectedItem } = useSelectedItem(); - const { setDroppedDecal } = useDroppedDecal(); - const [searchValue, setSearchValue] = useState(""); - const [selectedCategory, setSelectedCategory] = useState(null); - const [categoryAssets, setCategoryAssets] = useState([]); - const [decalAsset, setDecalAsset] = useState(); - const [filtereredAssets, setFiltereredAssets] = useState( - [] - ); - const [categoryList, setCategoryList] = useState([]); - const [isLoading, setisLoading] = useState(false); // Loading state for assets - const { selectedSubCategory, setSelectedSubCategory } = useDecalStore(); - - const handleSearchChange = (value: string) => { - const searchTerm = searchValue - ? searchValue.toLowerCase() - : value.toLowerCase(); - setSearchValue(value); - - if (searchTerm.trim() === "" && !selectedCategory) { - setCategoryAssets([]); - - return; - } - if (selectedCategory === "Decals" || selectedSubCategory) { - const filteredModels = decalAsset?.filter((model: any) => - model.decalName?.toLowerCase().includes(searchTerm.toLowerCase()) - ); - setCategoryAssets(filteredModels); - } else { - const filteredModels = filtereredAssets?.filter((model) => { - if (!model?.tags || !model?.filename || !model?.category) return false; - if (searchTerm.startsWith(":") && searchTerm.length > 1) { - const tagSearchTerm = searchTerm.slice(1); - return model.tags.toLowerCase().includes(tagSearchTerm); - } else if (selectedCategory) { - return ( - model.category - .toLowerCase() - .includes(selectedCategory.toLowerCase()) && - model.filename.toLowerCase().includes(searchTerm) - ); - } else { - return model.filename.toLowerCase().includes(searchTerm); - } - }); - - setCategoryAssets(filteredModels); - } - }; - - useEffect(() => { - if (selectedCategory === "Decals") return; - const filteredAssets = async () => { - try { - const filt = await fetchAssets(); - setFiltereredAssets(filt); - } catch { - echo.error("Filter asset not found"); - } - }; - filteredAssets(); - }, [categoryAssets, selectedCategory]); - useEffect(() => { - if ( - (searchValue.trim() === "" && selectedCategory === "Decals") || - selectedSubCategory - ) { - const filteredModels = decalAsset?.filter((model: any) => - model.decalName?.toLowerCase().includes(searchValue.toLowerCase()) - ); - setCategoryAssets(filteredModels); - } - }, [selectedSubCategory, decalAsset, searchValue]); - - useEffect(() => { - setCategoryList([ - { category: "Fenestration", categoryImage: feneration }, - { category: "Decals", categoryImage: decal }, - { category: "Vehicles", categoryImage: vehicle }, - { category: "Workstation", categoryImage: workStation }, - { category: "Machines", categoryImage: machines }, - { category: "Workers", categoryImage: worker }, - { category: "Storage", categoryImage: storage }, - { category: "Safety", categoryImage: safety }, - { category: "Office", categoryImage: office }, - ]); - }, []); - - const fetchCategoryAssets = async (asset: any) => { - setisLoading(true); - setSelectedCategory(asset); - 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); - } - - if (asset === "Decals") { - fetchCategoryDecals("Safety"); - } - }; - - const fetchCategoryDecals = async (asset: any) => { - setisLoading(true); - // setSelectedCategory(asset); - try { - const res = await getCategoryDecals(asset); - - setCategoryAssets(res); - setFiltereredAssets(res); - setDecalAsset(res); - setisLoading(false); // End loading - // eslint-disable-next-line - } catch (error) { - echo.error("failed to fetch assets"); - setisLoading(false); - } - }; - - const activeSubcategories = [ - { name: "Safety", icon: }, - { name: "Navigation", icon: }, - { name: "Branding", icon: }, - { name: "Informational", icon: }, - ]; - - return ( -
- -
-
- {(() => { - if (isLoading) { - return ; // Show skeleton when loading - } - if (searchValue) { - return ( -
-
-
-

- Results for{" "} - '{searchValue}' -

-
-
- {selectedCategory == "Decals" ? ( - <> -
- {activeSubcategories.map((cat, index) => ( -
{ - fetchCategoryDecals(cat.name); - setSelectedSubCategory(cat.name); - }} - > -
{cat.icon}
-
{cat.name}
-
- ))} -
- {categoryAssets?.map((asset: any, index: number) => ( -
- {asset.decalName} { - setSelectedItem({ - name: asset.decalName, - id: asset.id, - type: - asset.type === "undefined" - ? undefined - : asset.type, - category: asset.category, - // subType: asset.subType, - }); - }} - /> -
- {asset.decalName - .split("_") - .map( - (word: any) => - word.charAt(0).toUpperCase() + - word.slice(1) - ) - .join(" ")} -
-
- ))} - - ) : ( - 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(" ")} -
-
- )) - )} -
-
-
- ); - } - - if (selectedCategory) { - return ( -
-

- {selectedCategory} - -

- - {selectedCategory === "Decals" && ( - <> -
- {activeSubcategories.map((cat, index) => ( -
{ - fetchCategoryDecals(cat.name); - setSelectedSubCategory(cat.name); - }} - > -
{cat.icon}
-
{cat.name}
-
- ))} -
- - )} - {selectedCategory !== "Decals" && !selectedSubCategory ? ( -
- {categoryAssets?.map((asset: any, index: number) => ( -
- {asset.filename} { - setSelectedItem({ - name: asset.filename, - id: asset.AssetID, - type: - asset.type === "undefined" - ? undefined - : asset.type, - category: asset.category, - subType: asset.subType, - }); - }} - /> -
- {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! -
- )} -
- ) : ( -
- {categoryAssets?.map((asset: any, index: number) => ( -
- {asset.decalName} { - setDroppedDecal({ - category: asset.category, - decalName: asset.decalName, - decalImage: asset.decalImage, - decalId: asset.id, - }); - }} - /> -
- {asset.decalName - .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 ( -
{ - fetchCategoryAssets(category); - }} - > - {category} -
{category}
-
- ); - })} -
-
- ); - })()} -
-
-
- ); -}; - -export default Assets; diff --git a/app/src/components/layout/sidebarLeft/SideBarLeft.tsx b/app/src/components/layout/sidebarLeft/SideBarLeft.tsx index fdd4f87..ad26627 100644 --- a/app/src/components/layout/sidebarLeft/SideBarLeft.tsx +++ b/app/src/components/layout/sidebarLeft/SideBarLeft.tsx @@ -3,7 +3,7 @@ import ToggleHeader from "../../ui/inputs/ToggleHeader"; import Outline from "./Outline"; import Header from "./Header"; import { useToggleStore } from "../../../store/useUIToggleStore"; -import Assets from "./Assets"; +import Assets from "./assetList/Assets"; import useModuleStore from "../../../store/useModuleStore"; import Widgets from "./visualization/widgets/Widgets"; import Templates from "../../../modules/visualization/template/Templates"; diff --git a/app/src/components/layout/sidebarLeft/assetList/Assets.tsx b/app/src/components/layout/sidebarLeft/assetList/Assets.tsx new file mode 100644 index 0000000..5294be5 --- /dev/null +++ b/app/src/components/layout/sidebarLeft/assetList/Assets.tsx @@ -0,0 +1,177 @@ +import React, { useState, useEffect, useMemo, useCallback } from "react"; +import { useDecalStore } from "../../../../store/builder/store"; +import { getFilteredAssets } from "./assetsHelpers/filteredAssetsHelper"; +import { fetchCategoryDecals } from "./assetsHelpers/fetchDecalsHelper"; +import { + fetchAllAssets, + fetchCategoryAssets, +} from "./assetsHelpers/fetchAssetsHelper"; +import Search from "../../../ui/inputs/Search"; +import SkeletonUI from "../../../templates/SkeletonUI"; +import { RenderAsset } from "./assetsHelpers/renderAssetHelper"; +import { + ACTIVE_DECAL_SUBCATEGORIES, + CATEGORY_LIST, +} from "./assetsHelpers/constants"; +import { ArrowIcon } from "../../../icons/ExportCommonIcons"; + +const Assets: React.FC = () => { + const { selectedSubCategory, setSelectedSubCategory } = useDecalStore(); + const [searchValue, setSearchValue] = useState(null); + const [selectedCategory, setSelectedCategory] = useState(null); + const [assets, setAssets] = useState([]); + const [globalResults, setGlobalResults] = useState<(AssetProp | DecalProp)[]>( + [] + ); + const [isLoading, setIsLoading] = useState(false); + + const filteredAssets = useMemo( + () => + getFilteredAssets({ + assets, + searchValue, + selectedCategory, + selectedSubCategory, + }), + [assets, searchValue, selectedCategory, selectedSubCategory] + ); + + const handleFetchCategory = useCallback( + async (category: string) => { + setIsLoading(true); + setSelectedCategory(category); + if (category === "Decals") { + const res = await fetchCategoryDecals("Safety"); + setAssets(res); + setSelectedSubCategory("Safety"); + } else { + const res = await fetchCategoryAssets(category); + setAssets(res); + } + setIsLoading(false); + }, + [setSelectedSubCategory] + ); + + const fetchGlobalSearch = useCallback(async (term: string) => { + setIsLoading(true); + const allAssets = await fetchAllAssets(); + const lowerTerm = term.toLowerCase(); + const matches = allAssets.filter( + (a) => + a.filename.toLowerCase().includes(lowerTerm) || + a.tags?.toLowerCase().includes(lowerTerm) || + a.category?.toLowerCase().includes(lowerTerm) + ); + setGlobalResults(matches); + setIsLoading(false); + }, []); + + useEffect(() => { + if (!selectedCategory && searchValue?.trim()) + fetchGlobalSearch(searchValue); + else setGlobalResults([]); + }, [searchValue, selectedCategory, fetchGlobalSearch]); + + return ( +
+ +
+
+ {isLoading ? ( + + ) : searchValue || selectedCategory ? ( +
+ {selectedCategory ? ( + <> +

+ {selectedCategory} + +

+ {selectedCategory === "Decals" && ( +
+ {ACTIVE_DECAL_SUBCATEGORIES.map((cat) => ( +
{ + setIsLoading(true); + const res = await fetchCategoryDecals(cat.name); + setAssets(res); + setSelectedSubCategory(cat.name); + setIsLoading(false); + }} + > +
{cat.icon}
+
{cat.name}
+
+ ))} +
+ )} +
+ {filteredAssets.map((a, i) => ( + + ))} + {filteredAssets.length === 0 && ( +
🚧 No assets found
+ )} +
+ + ) : ( + <> +

Global Search Results

+
+ {globalResults.map((a, i) => ( + + ))} + {globalResults.length === 0 && ( +
🔎 No matches found
+ )} +
+ + )} +
+ ) : ( +
+

Categories

+
+ {CATEGORY_LIST.map((cat) => ( +
handleFetchCategory(cat.category)} + > + {cat.category} +
{cat.category}
+
+ ))} +
+
+ )} +
+
+
+ ); +}; + +export default Assets; diff --git a/app/src/components/layout/sidebarLeft/assetList/assetsHelpers/constants.tsx b/app/src/components/layout/sidebarLeft/assetList/assetsHelpers/constants.tsx new file mode 100644 index 0000000..fe06ecc --- /dev/null +++ b/app/src/components/layout/sidebarLeft/assetList/assetsHelpers/constants.tsx @@ -0,0 +1,34 @@ +import vehicle from "../../../../../assets/image/categories/vehicles.png"; +import workStation from "../../../../../assets/image/categories/workStation.png"; +import machines from "../../../../../assets/image/categories/machines.png"; +import worker from "../../../../../assets/image/categories/worker.png"; +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 decal from "../../../../../assets/image/categories/decal.png"; +import { + AlertIcon, + DecalInfoIcon, + HangTagIcon, + NavigationIcon, +} from "../../../../icons/ExportCommonIcons"; + +export const CATEGORY_LIST: CategoryListProp[] = [ + { category: "Fenestration", categoryImage: feneration }, + { category: "Decals", categoryImage: decal }, + { category: "Vehicles", categoryImage: vehicle }, + { category: "Workstation", categoryImage: workStation }, + { category: "Machines", categoryImage: machines }, + { category: "Workers", categoryImage: worker }, + { category: "Storage", categoryImage: storage }, + { category: "Safety", categoryImage: safety }, + { category: "Office", categoryImage: office }, +]; + +export const ACTIVE_DECAL_SUBCATEGORIES = [ + { name: "Safety", icon: }, + { name: "Navigation", icon: }, + { name: "Branding", icon: }, + { name: "Informational", icon: }, +]; diff --git a/app/src/components/layout/sidebarLeft/assetList/assetsHelpers/fetchAssetsHelper.ts b/app/src/components/layout/sidebarLeft/assetList/assetsHelpers/fetchAssetsHelper.ts new file mode 100644 index 0000000..9fb678e --- /dev/null +++ b/app/src/components/layout/sidebarLeft/assetList/assetsHelpers/fetchAssetsHelper.ts @@ -0,0 +1,22 @@ +import { getCategoryAsset } from "../../../../../services/factoryBuilder/asset/assets/getCategoryAsset"; +import { fetchAssets } from "../../../../../services/marketplace/fetchAssets"; + +export const fetchCategoryAssets = async (category: string): Promise => { + if (category === "Decals") return []; // handled separately + try { + const res = await getCategoryAsset(category); + return res; + } catch (err) { + console.error("Failed to fetch category assets", err); + return []; + } +}; + +export const fetchAllAssets = async (): Promise => { + try { + return await fetchAssets(); + } catch (err) { + console.error("Failed to fetch all assets", err); + return []; + } +}; diff --git a/app/src/components/layout/sidebarLeft/assetList/assetsHelpers/fetchDecalsHelper.ts b/app/src/components/layout/sidebarLeft/assetList/assetsHelpers/fetchDecalsHelper.ts new file mode 100644 index 0000000..b0f1be4 --- /dev/null +++ b/app/src/components/layout/sidebarLeft/assetList/assetsHelpers/fetchDecalsHelper.ts @@ -0,0 +1,11 @@ +import { getCategoryDecals } from "../../../../../services/factoryBuilder/asset/decals/getCategoryDecals"; + +export const fetchCategoryDecals = async (subcategory: string): Promise => { + try { + const res = await getCategoryDecals(subcategory); + return res; + } catch (err) { + console.error("Failed to fetch decals", err); + return []; + } +}; diff --git a/app/src/components/layout/sidebarLeft/assetList/assetsHelpers/filteredAssetsHelper.ts b/app/src/components/layout/sidebarLeft/assetList/assetsHelpers/filteredAssetsHelper.ts new file mode 100644 index 0000000..38d2b39 --- /dev/null +++ b/app/src/components/layout/sidebarLeft/assetList/assetsHelpers/filteredAssetsHelper.ts @@ -0,0 +1,34 @@ +interface FilterProps { + assets: AssetProp[] | DecalProp[]; + searchValue: string | null; + selectedCategory: string | null; + selectedSubCategory: string | null; +} + +export const getFilteredAssets = ({ + assets, + searchValue, + selectedCategory, + selectedSubCategory, +}: FilterProps) => { + const term = searchValue?.trim().toLowerCase(); + if (!term) return assets; + + if (selectedCategory === "Decals" || selectedSubCategory) { + return (assets as DecalProp[]).filter((a) => + a.decalName?.toLowerCase().includes(term) + ); + } + + return (assets as AssetProp[]).filter((a) => { + const tags = a.tags?.toLowerCase() ?? ""; + const filename = a.filename?.toLowerCase() ?? ""; + const category = a.category?.toLowerCase() ?? ""; + + if (term.startsWith(":")) return tags.includes(term.slice(1)); + if (selectedCategory) + return category.includes(selectedCategory.toLowerCase()) && filename.includes(term); + + return filename.includes(term); + }); +}; diff --git a/app/src/components/layout/sidebarLeft/assetList/assetsHelpers/renderAssetHelper.tsx b/app/src/components/layout/sidebarLeft/assetList/assetsHelpers/renderAssetHelper.tsx new file mode 100644 index 0000000..2c27f79 --- /dev/null +++ b/app/src/components/layout/sidebarLeft/assetList/assetsHelpers/renderAssetHelper.tsx @@ -0,0 +1,58 @@ +import React from "react"; +import { useDroppedDecal, useSelectedItem } from "../../../../../store/builder/store"; + +export const RenderAsset: React.FC<{ asset: AssetProp | DecalProp; index: number }> = ({ asset, index }) => { + const { setSelectedItem } = useSelectedItem(); + const { setDroppedDecal } = useDroppedDecal(); + + if ("decalName" in asset) { + return ( +
+ {asset.decalName} + setDroppedDecal({ + category: asset.category, + decalName: asset.decalName, + decalImage: asset.decalImage, + decalId: asset.id, + }) + } + /> +
+ {asset.decalName + .split("_") + .map((w) => w.charAt(0).toUpperCase() + w.slice(1)) + .join(" ")} +
+
+ ); + } + + return ( +
+ {asset.filename} + setSelectedItem({ + name: asset.filename, + id: asset.AssetID, + type: asset.type === "undefined" ? undefined : asset.type, + category: asset.category, + subType: asset.subType, + }) + } + /> +
+ {asset.filename + .split("_") + .map((w) => w.charAt(0).toUpperCase() + w.slice(1)) + .join(" ")} +
+
+ ); +}; diff --git a/app/src/components/ui/inputs/Search.tsx b/app/src/components/ui/inputs/Search.tsx index ff9c6ef..73db02a 100644 --- a/app/src/components/ui/inputs/Search.tsx +++ b/app/src/components/ui/inputs/Search.tsx @@ -1,8 +1,8 @@ -import React, { ChangeEvent, useState } from "react"; +import React, { ChangeEvent, useEffect, useState } from "react"; import { CloseIcon, SearchIcon } from "../../icons/ExportCommonIcons"; interface SearchProps { - value?: string; // The current value of the search input + value?: string | null; // The current value of the search input placeholder?: string; // Placeholder text for the input onChange: (value: string) => void; // Callback function to handle input changes } @@ -22,7 +22,15 @@ const Search: React.FC = ({ onChange(newValue); // Call the onChange prop with the new value }; + useEffect(() => { + if (value === null) { + setInputValue(""); + handleBlur(); + } + }, [value]); + const handleClear = () => { + echo.warn("Search field cleared."); setInputValue(""); onChange(""); // Clear the input value }; @@ -48,7 +56,7 @@ const Search: React.FC = ({ { const { camera, controls } = useThree(); + + const isTextInput = (element: Element | null): boolean => + element instanceof HTMLInputElement || + element instanceof HTMLTextAreaElement || + element?.getAttribute("contenteditable") === "true"; useEffect(() => { const handleKeyDown = (e: KeyboardEvent) => { @@ -21,6 +26,8 @@ const CameraShortcutsControls = () => { const dir = new THREE.Vector3().subVectors(camera.position, target).normalize(); + if (isTextInput(document.activeElement)) return; + switch (e.key) { case "1": // Front pos = new THREE.Vector3(0, 0, distance).add(target); diff --git a/app/src/types/uiTypes.d.ts b/app/src/types/uiTypes.d.ts new file mode 100644 index 0000000..16cb72c --- /dev/null +++ b/app/src/types/uiTypes.d.ts @@ -0,0 +1,29 @@ +interface AssetProp { + filename: string; + thumbnail?: string; + category: string; + description?: string; + tags: string; + url?: string; + uploadDate?: number; + isArchieve?: boolean; + animated?: boolean; + price?: number; + CreatedBy?: string; + AssetID?: string; + type?: string; + subType?: string; +} + +interface DecalProp { + id: string; + decalName: string; + decalImage: string; + category: string; + type?: string; +} + +interface CategoryListProp { + categoryImage: string; + category: string; +} \ No newline at end of file From a14f7fcf6aa5be28f197ebdc5c3d84b6ee8d2b20 Mon Sep 17 00:00:00 2001 From: Vishnu Date: Fri, 29 Aug 2025 15:14:24 +0530 Subject: [PATCH 27/34] dashboard kebab bug fix --- .../components/Dashboard/DashboardCard.tsx | 74 +++++++++++-------- app/src/styles/pages/dashboard.scss | 67 ++++++++--------- 2 files changed, 79 insertions(+), 62 deletions(-) diff --git a/app/src/components/Dashboard/DashboardCard.tsx b/app/src/components/Dashboard/DashboardCard.tsx index 8ac05b6..7dfb283 100644 --- a/app/src/components/Dashboard/DashboardCard.tsx +++ b/app/src/components/Dashboard/DashboardCard.tsx @@ -1,4 +1,5 @@ -import React, { useState, useRef, useEffect, act } from "react"; +import React, { useState, useRef } from "react"; +import { createPortal } from "react-dom"; import img from "../../assets/image/image.png"; import { useNavigate } from "react-router-dom"; import { getUserData } from "../../functions/getUserData"; @@ -7,11 +8,11 @@ import { useProjectName, useSocketStore, } from "../../store/builder/store"; -import { viewProject } from "../../services/dashboard/viewProject"; import OuterClick from "../../utils/outerClick"; import { KebabIcon } from "../icons/ExportCommonIcons"; import { getAllProjects } from "../../services/dashboard/getAllProjects"; -import { updateProject } from "../../services/dashboard/updateProject"; +// import { viewProject } from "../../services/dashboard/viewProject"; +// import { updateProject } from "../../services/dashboard/updateProject"; interface DashBoardCardProps { projectName: string; @@ -69,9 +70,9 @@ const DashboardCard: React.FC = ({ const kebabRef = useRef(null); const navigateToProject = async (e: any) => { - if (active && active == "trash") return; + if (active && active === "trash") return; try { - const viewProjects = await viewProject(organization, projectId, userId); + // const viewProjects = await viewProject(organization, projectId, userId); setLoadingProgress(1); setProjectName(projectName); @@ -96,13 +97,13 @@ const DashboardCard: React.FC = ({ case "open in new tab": try { if (active === "shared" && createdBy) { - const newTab = await viewProject( - organization, - projectId, - createdBy?._id - ); + // const newTab = await viewProject( + // organization, + // projectId, + // createdBy?._id + // ); } else { - const newTab = await viewProject(organization, projectId, userId); + // const newTab = await viewProject(organization, projectId, userId); setProjectName(projectName); setIsKebabOpen(false); @@ -226,7 +227,7 @@ const DashboardCard: React.FC = ({ className="dashboard-card-container" onClick={navigateToProject} title={projectName} - onMouseLeave={() => setIsKebabOpen(false)} + // onMouseLeave={() => setIsKebabOpen(false)} >
@@ -267,7 +268,7 @@ const DashboardCard: React.FC = ({ )} {createdAt && (
- {active && active == "trash" ? `Trashed by you` : `Edited `}{" "} + {active && active === "trash" ? `Trashed by you` : `Edited `}{" "} {getRelativeTime(createdAt)}
)} @@ -292,22 +293,37 @@ const DashboardCard: React.FC = ({
- {isKebabOpen && ( -
- {getOptions().map((option) => ( - - ))} -
- )} + {isKebabOpen && + createPortal( +
+ {getOptions().map((option) => ( + + ))} +
, + document.body + )} ); }; diff --git a/app/src/styles/pages/dashboard.scss b/app/src/styles/pages/dashboard.scss index 01024d0..cfb2770 100644 --- a/app/src/styles/pages/dashboard.scss +++ b/app/src/styles/pages/dashboard.scss @@ -239,38 +239,6 @@ } } - .kebab-options-wrapper { - position: absolute; - bottom: 40px; - right: 40px; - background: var(--background-color); - border: 1px solid var(--border-color); - border-radius: 8px; - z-index: 100; - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); - backdrop-filter: blur(20px); - flex-direction: column; - transform: translate(100%, 100%); - overflow: hidden; - display: none; - - .option { - padding: 8px 12px; - font-size: 14px; - text-align: left; - background: transparent; - border: none; - color: var(--text-color); - cursor: pointer; - transition: background 0.2s ease; - text-transform: capitalize; - - &:hover { - background-color: var(--background-color-selected); - } - } - } - &:hover { overflow: visible; @@ -337,4 +305,37 @@ font-family: #{$font-roboto}; cursor: pointer; } -} \ No newline at end of file +} + +.kebab-options-wrapper { + // position: absolute; + // bottom: 40px; + // right: 40px; + // z-index: 100; + background: var(--background-color); + border: 1px solid var(--border-color); + border-radius: 8px; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); + backdrop-filter: blur(20px); + flex-direction: column; + // transform: translate(100%, 100%); + overflow: hidden; + display: flex; + flex-direction: column; + transform: translate(0%, 0%); + .option { + padding: 8px 12px; + font-size: 14px; + text-align: left; + background: transparent; + border: none; + color: var(--text-color); + cursor: pointer; + transition: background 0.2s ease; + text-transform: capitalize; + + &:hover { + background-color: var(--background-color-selected); + } + } +} From 1b161b21765701d4c054cb7ccd33eb32487e997b Mon Sep 17 00:00:00 2001 From: Vishnu Date: Fri, 29 Aug 2025 16:24:03 +0530 Subject: [PATCH 28/34] - 404-page added and fallback for project not found - page nav handling for project-not-found updated --- app/src/app.tsx | 2 + app/src/assets/image/404/404.svg | 9 +++ app/src/assets/image/404/404_bk.png | Bin 0 -> 233749 bytes app/src/assets/image/localAssets/arch.png | Bin 40000 -> 0 bytes app/src/assets/image/localAssets/window.png | Bin 80000 -> 0 bytes app/src/assets/image/sampleDecal.png | Bin 35553 -> 0 bytes .../selection3D/boundingBoxHelper3D.tsx | 2 +- app/src/pages/PageNotFound.tsx | 57 ++++++++++++++++++ app/src/pages/Project.tsx | 3 + app/src/pages/UserAuth.tsx | 2 - app/src/styles/main.scss | 1 + app/src/styles/pages/pageNotFound.scss | 56 +++++++++++++++++ 12 files changed, 129 insertions(+), 3 deletions(-) create mode 100644 app/src/assets/image/404/404.svg create mode 100644 app/src/assets/image/404/404_bk.png delete mode 100644 app/src/assets/image/localAssets/arch.png delete mode 100644 app/src/assets/image/localAssets/window.png delete mode 100644 app/src/assets/image/sampleDecal.png create mode 100644 app/src/pages/PageNotFound.tsx create mode 100644 app/src/styles/pages/pageNotFound.scss diff --git a/app/src/app.tsx b/app/src/app.tsx index 9685c13..bbd5984 100644 --- a/app/src/app.tsx +++ b/app/src/app.tsx @@ -7,6 +7,7 @@ import UserAuth from "./pages/UserAuth"; import "./styles/main.scss"; import { LoggerProvider } from "./components/ui/log/LoggerContext"; import ForgotPassword from "./pages/ForgotPassword"; +import PageNotFound from "./pages/PageNotFound"; const App: React.FC = () => { @@ -23,6 +24,7 @@ const App: React.FC = () => { } /> } /> } /> + } /> diff --git a/app/src/assets/image/404/404.svg b/app/src/assets/image/404/404.svg new file mode 100644 index 0000000..d17802e --- /dev/null +++ b/app/src/assets/image/404/404.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/app/src/assets/image/404/404_bk.png b/app/src/assets/image/404/404_bk.png new file mode 100644 index 0000000000000000000000000000000000000000..27bfd56ac693d6118a57810eba93a5f091de89f2 GIT binary patch literal 233749 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

ZfL%r@RI;`??IBwe?10eXtBD1%FTPaku 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�(6paT)hELC(Sc1HBX2ys<}K-ev) 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

7nAsN?wxXL$vDoQhE=ZxsF?PLeQ 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≀N`y zj54Y@W40w~V#47R9*j(h$%s=F*M`RYxzkU5qAZ=jcxK_T+BffW*xK3(e(e^z4 zgTyZ+&QtA`(v#B3Jri773|k=E6{bgC+%?%mJP6`)c}k60N%)Y)nZ%Hn_8nafZ)rZc zx=7{MhT(RdOOq-O0Pi%kph(ryKS0NTt|RVCAae}p7>AYg{ajn&$}atD31^7T`_vnC z9c`Ua*Gs(%3|#CdB)X)q%&{b0-#%Fy&$SilwPP8>gmG(OU^@W=6CXHFuAE$$$;fcP z&nAV6Nte<|{c`E7Fg^9l9dq%iymI+LBE1BCo;k&upajR|2ks21^DYl#sZpKE2fvit zu%VH9$>flG*_Py6B8y+vUGwG~$rQOUfs5ouOmKt|951}^!mHiyvrYMYnBN(-b@;s# zUqw3O(Fm=P2*7_ClNg=UIUY{DivC)HVF~BS3Ou@G75&D9M*DjL4@io3`ps!{-SIjj zE8vY~XzI-qMmtcGLc7iJy!mVI%{00<7TvE{rb6`#(RJY+IAUGYAE09Zx?KUJ+ZlJ9 z(vb?2#B~AXNW|e|N{Xwpca`!rQ|GDeM@u`JIx%UkBF5>bA z;OmZR%K-fK8YVpeUyp`JhmW~F-XmU5#WMQ2w#ezUAny}!K3GSt{orfNmBS|TLtLLEeMug;E>ea{&bYww@ryTAo|cfXKpT0hD81h`sj&KX2l4m&jXJO^8NPNmPiYq)eJWsw-MCGyg| zO466)N%iHNOdL+-VKc|nA0)ycQBJCj;pd0yyK>Kd>Yk?VD^+$Y{L2v zrOr!1rI*|rAY^jwPMSX#FR{a@^2Ntg`=E4MDDU^U@=(XrIXCj7G@m$(x(1Vp&*hQ2 z*QsY-(gq~ytRP>m|Dob@I)OQtqVF?`k$MLzMt~9=4<|V1Bqi6vX5=LgFm@t`bXM?- zwU3UH^xSnz$dOo9Qh$^o8P{Wc-iF~(0I}iLI@$4>4gSxMEbxx}=iT40xxX7wk38^> zI-X(?kLXx7y5o@%E9NgA0YSy#(HD5+MtmIW!=o!IR9|?+N=!Di*A(kaksB!(lqYu& z3_6a}0$hAfDwIe3EXBtak#GD5#4*mB3d_kDVOq}Fk@O2G2Y(CUomp&4GFrxOYsd{I(!} zD=1H@+?J3p$xos?_dIZ6xqD|j9s{|TuOxk}r;EN4<0bWCmE?izmLojczj!1^Omwv8UdB80e#Z_^0NjO| z+?W!2S~4IWF@dLjP-I2?XA^&rh{GgGJaXd{m8FHh&Uh+Ld^{oaLMP+E~FD`p(-3>$JNBwfgLy{XgM_&rc@XYD3Oxjc)xD5dC zZ9ocae2&pKmi8xHm@Q0Of?Pf}5m%}scP=%EOJ!4e=47d3&e)I7xjqyU@mM#NSKNnC zeIeDJrER;U9dhNig@36jDO@-}dTP%QZ6nvWrACT%eO{@2r8*>bpe@XY(of})vz>DF zPU(*0avwD;o^AoazvS@ON1XqX`K9I+$1m#kd>ZA)jr@r9;L#KY z$+iSXOKzA*cRSi89%T{h!pAtQE!!m0X_)eGe~|di<)8aa^#i%Q;(C`nV-f?aByAt$ zjO;%8B)U@$hGq1ol6AyO0}HJyOR8==+50sDz}jz}N8*!0*NuT-!|b zIl1_*WiPc;tw1i7zGBJ{-*4`nhuf^AK1%y%D3DF%!Rf9t=P5m@V=4}Z<2WdY1IU*z zmsS!#bUH;3D8X_0kTdd2bmU${5Xo`QN-iJjMULZu$d5|X7gUGdH;-JWTB4tNDaPR@ zAs@@848_>7ji*M$=kdA?egwyTctD1*0Z3GiAS@yLm|9@{tr4@ix83XF})+UDI76cZv}Nc`f_8*#Z}9o!$5;o2ZoAC?G9 zodeL%9**ntOiI$)Lis+(J;QBS7XwIIS8@AH$)wKvhLyypzeBiwkJ1T9Czj=O;Cy1;F|mEf+EXYU>9FHAyTo3-wkKAZSEwocq;^D~HAf$*5{I$$7>2W!^7p7(YAS!$N+m>~L z!X`jF`GEAqI;L=*TxhQi7+n*TuSIDfI*&;vlh7I8vkK0S-iYr@OjrTRkJ5$n0RYly z!|jkWDyegu35)ZhAbozMhQ+!KkiLD3q(A9%?UFEw(I_bc)%WIP<8vb{3l~>X#x7wg z8QU`+hf)115Mk0jnEK7tH{}!>kl*K=GfJIHt}70+jI3m2Mk+j&Z(LSMA7|lWeFAiU z)^VOxA5Q7x?kAR`&W#{EC&S6%a#z5=uTt%avNIuZ&c#vWMtdx|-vE66C4Hmx-f&^k zcV*B6NqlMgtK z111aN(HmHaSAqRG5K$S-G_|45ZiD;O-06_N`Dy9LBYyRCyqimn58PFS-0|fj;ip z;o`0EmqN+2Ceg8ubcz9?1jpqA_u}ZQPpUKSfKx9VoTd-f>CnV4Q~bi(G*T~m>Pwl) z=*k%)v7y(J9I?ZVo#j}bI>vv%oC9$TOgv8CB6jBSl$y5u6XrS~k#DX|P^18*zpb10 z7_V!BpGiEzqt%^y;ZueL&I9L%(^JF+5c0Tfa!x!KPu)Kbn|v0#I%!>jpHHryq=w!Q zak(}|wRPx_4<{>qR;BW(^rc~a^x-&3y3}(|^_B54caC)!;vCDRo?)t=0N}|H>VD;} zJN3(j_5FiHSgzb0a?g7VBS>Qt^11evBbF_sNsLrJDR}_8ElJv1o0e;@u?&^gBe%{* z?mdZhNa{Kc%heZ^z9f&7jI_O4fsR}lB;u_gE|rGTEj`bL=g>8sVqhr2aXGI^xR+suS!|S0(x2mt-nU%Gi(?MN*yquU!mt+WZ2X-wg!I$LUIpL;%^#M;$x3oeb_aI<90)(%iX&!_cm9y-1CjoF3vJ>xX-njcrHCvM(&-&y*s;HFISe_wZt;1KM!u?233w!y-3rmJr(4gd@4TIu7QY`v`L(&i^<3HI{JRIi3Jdiddurh~v~t%c8P!GB_Sz3q^ErqyfICltD(lz$Cij^0vFw zV51~nL!2^b+TxQA!kgE z(KqGVBPTBn&&lKrTP{qJR%)P@fNHxrd8ytmaZ;I-6F^;$6{OFF*-RX#!-E^e#Pxac zGebRphS9^Fb7f1F9lw*Puv8s`P&QTX+&N^@Q1wd5QT5K{Q}UePv|VL6!h?F>*hCnW zj|KU27VC@KB_~^o+`B@OmP?Q8h9Z2V`lVr#cniFpmP^OQ;bWW8mC6eQ4=+4$f$Q<-+52wy zZS1s+q(=7Ay*&%HZdpsRNQ#cMLiD)YBQ}KS5Gv8!JM3Q%80WVey6@UR7wpfOX$ls zP|^(}N9a{LKdP$H2hsFCm#*pJ9%Lvh#xvU61^lY^O{u1ymQ6ppAnl|51ATKDOgT|k z4f&SxmASWz5A!ZZ?suQWi#=B=pN<4Fd}M#lJUU!b@z}fMhYtPNOBH0dJLb+RpZY0n zy~R(;?&`|yfIm=QEv~55;x=LNgqB8&NC$^_OvD1jzo&F+kBJwBwHv=e9>J<7)2u8mTeI?^M1?>djmzg`BFL4(@&X z0wdmy8jw0d>C=<$@=Z=O>f0}W?v$w)NsVANlpuF#AKsDTu-HGSLP$a98>92LiG3VuyQr_8rv`SZD30IPMmo~1DJsqsi}$Gneao=?->zcG7kT$zVxpHws` zWPM(>CTm6v;wYQC!V*zEhdR#{8($_X`X#-#7u4wL;Vp6>BfX>f%|+<%?o`csxBg|eFqwq!{r&V z?=1a+yT6SxM8|*!61aX5E3CiAlN;jb-|zH3Z@=5!kw+c3eMS3Xlu#{&#cObk5$p8r zt1e3S*BSZ{%it&^d#1i<={BqR=$+;3&Z@=i{Y>Z-*5k(>-QwJq*vFBFdQ<1O6`nf8 zW(U4TZCk1!j!fDyW=yzF!2A&fEo@){nz z<=(__v$an*%~BPLu=-8O1^UCe+3Rj5<2vUh1$B=2U0IMP&YANIqs|>&{)-j3+^gKR zac`|f6JzDEMRP+HABD(0gMH2|?LOQKohmBxkvU>Q#uSpY{?v|f1|c18p&+1u4nNn5 zTsn>Yf(q%gywPxdk4-b}S>7uo3K*+g*-YD<5Mh&j$^x>Q7U+O>RHXKthF5V2n~g&e zoB3$rl&dG$@%I~xd~US|fHzO%S$XD57D7L=t(-oc`j}t=4sG~{wu%y^JJhHHVsL{P zsR}u~?Z>xCQsh&QoD^im!hI{it;qENzW#sRuSPf-ZUww%G)M-$yV%eABAM)oQpz24 zmQ7X~NhmeI`j6=TG_S7P!VY_eeU*N+is~}e+cQ~BpYgz&S~M3OMSq5i5>ahuv~&4J z`y-l&Hi5xkKShU{(oX@Sy>q{6;VvU`mJWHm4L5TTD{tKc`EWfA4c~CM(@So;PWp*1 z7IbG}W0A|HT)ElE(AP5!K2XMH20#6K-Vz^Uv)h|EvR@+^(7C)8%^KWgBw6UQnwIp< z+C;RLaK#L2z%+OU3*V5?I_pSPJZ--{L0Qb}+{p0^6DChJJZN}&NX_`cc5}O)dCPF% zx23lmS!*wb{I3W-PG!6C3yKG5@l6=MF@0*8Rq+IE>#hAoYFY&!Fb@~Iz_vyUK(cIl zHA8V2+tZak6Or)Ivm)rv`*MzQtc!P)?NTuow((Nf0XU>^WNayjdzLt#d*EtYmr*&r z8+r&OI;2kLP#5J_4DxGa3w$Qob-yi7_c(6bgx;sQJZ>(&!a+9xDqrOd;YN!thL!i~ zWJ|57sIn__ez}ah&G@VASxy%H(8Y6neIARy%3D)h%DcAGTZm-W1wd8RIZPz`(8pwW zk;(EaesJgxw`@$gyrf~VAMbX!rq_LYv{PtSbPd_zfq@#TU9#P`c2$N& z4hTKmHKIqTdxn`;$As|*jPDztzF&EZc}+Rz=+evmw+GdnD}C5Tc(+OuKKQ}SDW2)Q}u|0!fj)Wq)$1xXre!6*A*>%#2csL#H|Ja#;YexPfEQDud} z7cG%4F|QYc?I^d(#v}paeR0=(Fg&U~UX(Ks6x~CmDx$cwbJWq{qBg z(BX>dcn|w9(OjsYji3-aBEWaKlz0CFY(@sob~Bu?oC$8&F>GH$T^Yo^zh?&AzySk$ zj-mU&(bF$~wLmd+LN%zUSK~&J&Y=x(0h^*NNKjcP`#l6@h`dbd@}bgR!J0P%Z_(@G zSepWQ6q*%s{Ay36O_B%4B}W(*__*1$qJk{-c0dn&LW=g|)E&&u*Wc%my`WL&xqGwN z(5A%s?Gui-y<#DmOR5!Pfx9=-8d^g+(r{w3{OXlLx1Iydu1fe24;RZhx5yO(Q<&l3 zPhr!K+71w-6Q9^x5r-UjE_W*_l4@#RRSYB8gja@-xvs$#lp)yVv~JgDjml?x1~(;c zhh&EY$vRrG-jouDq4r-S%!SZCkPL+0XkL2X=<(64p5)S9|DyKQuZL@Bih}qeH@hf`je}s6x^& zz3*Pw#)cjDZL1sta>oGZ!2Q`gH`)6q;Hy+3~Zw)zon(O`@vB z1}Zjw@f}=oPuxN1z317}XXVal>tyQXHGPv=Rr0ppZWiNDGDsP*ns0bh63n>Sx1eRW z_?nc4ysKtAqaPaM@nfx-=;IGL#aMIU85R6)E?G@&!wy{QdELv)oI@0~us8Xoi?7_G z&-;eZFljm>yFQ?>1kLW(!Ho;!XIg_Et|Q@D#_vjkbWH-*BV4Zi zm>i_7qEzCh^k$YmwTP5+?U4_Nn^3BD-Uk;rR?&(ww7<`!S%Dit3)}q*pSm1Fu+#XE zBa}Ue2#Z%^v^IU)$%nePMSC3;&EKd1BP3mNN zOxWA68H!?z?XcU(MTv@~v@v30`*m%6Xgs}SvCR-eBz~9tvOm8&sE}Cqky>+pX5yED zi^sI@F!@fT8zZtere)wzw1p|lCniwQhYEUadj=ut7Je~hM}OqRWpjm!NfUI_*G0sl zCenJN$Ds4uOWL2j?*%S4at>3UlwIqcz+%{taY8oEa& zJzX$8yJ?rcoh%80uRYu`5yzG7Lav&$`imC@d9OQkcxpO5sOQS4&3muoMzj)yQuNch zr;3iG-JF>r5V%`&DI$EKmZSzI(X?bda0*`d7_!kWJB05^YbJz;#viA-bc+*zX$8$6 z&&m0f3&x=1+4T=ZX5n=7gNd5NZ&Q=7w>0OLqrESQlIMMkZ)jU?W@*MiJlZdapjay6 z{wHug+fFYuS%gxy2T1(%?NvI`U!e%8(~^1j{o(Vxk* z8mC64%dFdm7G}gCl)v2o=R2{J6?;8m;I{B%Lo60{_ziyC&|y1DMelLMvISk|$aUq< z7io=H!=R0S=>x7jyqk}^W=rW?nYL6-9HdI5^Hn~iG1BEy|4{#3Rb5l)L!pWcgv(1V z1uIS?9NFBzzi+QvTaLWcUF*?P`ty5uw}SYAwx9QuokPtW zUwKz9>eVYW7uAkg*?=$UXYctl|1nxLSEgLDwxkbF{4k`hk7jMltx>}km1rrM1}Zo#$kstSBi5Dt>bdD^!qVX; zi#}L@t_@#MO07q*3}+d!R+@4^n`wLK184KoPWh6}#VWOU&DNll=7{b~$eOv`us+|V z`{jOSct2&vKYD5(?4s5k+a%-}K^asDVYg1^4QD;OC&iHqdy^=Vky@k_RsHG=+c$SD zrd5X2gqN^IB0mrbLxVWT25jM~fqsrlZvQTNc_gC(n4@0)0c z=v?FgtD3nmd%wIt^1G(pHF#8>nHPN($lWG1dcmL*;8lf&c>1BzhlpW{+w`|7Qx}qw zs-oB_&Rb&owdQU44iE6s+e~L|SJ~ud zZ;yEvHgFx&?flgwzpVS?06il{mxyZkD7L&<|l0L^F#NI1h3}Tm!jM_Ck0l#&k;yb;h&lZ?xUGk&%3_h z)D&rTzyJXuMLCF9eBd19J4Z1~{Y}MYv3{cNoxO58GV6@XZI@#eZJ-%UL!BS^jU@IS z(zR!$v!WW`XA-xE#Biv{w2GO19$&0boLX8P2kWuzCOpwBnBFIr?N|mWzCxjj!*!t0`6Xa zQW;1u^Z}X5{rlA|P$+=!llU}s??bE@(!lo1-9B9v2!Yg--a5Ej~5Bw1@#&NlWJ6Ez8(PFw`|k!ja#QGl=5H z&~X`5Ol!~FjH%j_cKyuRNUp2)@%$47`nkdL6Y(S$YFqGE#)P=6dm9B z_C9AYnY&;U;A@%9|1*g%wN4jTz4OV>bGDJ7cern)n%$FX*hKFfJG$uC|HE*1V z!e2u)Uynmvbo!i@EVBz&RYKHJKHcY-&tlxyB5B8+=ZbTzFYG9Cm;z)JAYWAS zfY2PU`hcUf=x6_|XHD0el5Jut59#!j^+kU4x|m{B;i@EJ2V`2LCGTH~7VoqvUed(a zMEY+a*>k-koV%$TaKD8yHzaR=xA`WOx^CiCOfWUc5L;A|wfA52D`=fX%Sd}IzN(s) zRqBg)q|o^LRr-m2aOwPcDr#7zxKzPCw4rCgxP8lQlSy-C^5F}`AX%#MpiPeimBJ@J z1vGv;O0^WIJ$d~s@vG-PUXysAM=W+g+X_h8ocn&a?@(gMQsph7LVO=ISn;6BDe^mU zUUK7?G#xR3b`Qzd_e6Y8hxj*v127Fe-SXBCOszuPGXKmV@P#wPC$rSGV%5Vau0XVR zWTIWA0&8cVa#J?S26W_u2^0AM(x1;ie&KQ^##a(QR!}DTyHugiz_CK%@e%@xVML*~ znOtjWR_Pka??iOO-bQmL#?{)^I&YUpZ6Z;atpM(}HgE0pYc*^r;SQQYbM2SV4I)aT zf4eQ%K%&04b*Wnuh_c$nlF%eoAd#@6Ed!D@Qs25xYhMPMCbAt-gD6mS=e9| z5Y*#yCL{D8W1Jjtx*y3|Y^&<*qSDp0jlCF1 z=nnPuU0_75!H~&kaaxDjhNcvVJmsd@c|++#k`+ksNU?do*(&=V<1N)Cn+P{U^)lDM zS3xssxFUAQVKdf0dwPMUIYp!%Cdb?Shxr{DPi-(_01uElq=bf|-` zXrH9u@rtrK3#!L^p8uuWAxQ!BX}TG$L7TAR)_>Z+Rc-#&gN#?8Q;_C$q#ozSx$azP zvKXWa9Uo+h@m&JvS4;{+Tfr`rTV4n6Qmxd;I`1PhtZwTnO63?v#e;E%WGCsfNeMuW zrbvg~(_xH^oED6B&{eh=sz@@0DDlSxW=sd?lkaV7_w{rJNG9+ldtlX7l2ueJK~sP+ zynsR|EC8iSr8_;fxL~?OX2CkN?1k-uHG>n3&>Uq2`TH zbz5O=@>7_0(w7=Wc)c%56r9TzLF@APbVN(QQI#$CVz$(6$@cFfNK z1L;hM+KJH`lqgSZ@8l4;)-aO;aUNP*^ zmb$l_ksT-#c3{XF+D;E;t)XwGPJ^s_YaouUvf8ZL7qmP3V-#uqP?D-=Hfr|GA@>{+ zSJ{7|HfnVL2hj`jjH+23oH8i((`tKcm+_vi{x&hGqV(MM3N~%AMSs;a1897JUuWsz z7xgtXRE$w8;AxD4y8rAcM3MG^zyQtZp$>_e z)z;9*K~xis>=2K+@elPVG6eWW))HZAc7dl}p=+^>=6LMpENQsK7h6v6Eq1ld$Rqb%^!aO>GY zzy64HOaaDOsm!s5*nWacM@xsJW0t9?eC?Q@_y}dT!V;B8R+gds(7H=g8!ebhVG3#ptdv&F?p`A^D3l z@MKB2Pzvc*THDS<k9#zNI)YliUUJDk>^jqP^=4l<$E}f4~%6 z)bY<*>j&hr@RVANn1DmPx9Vp%0WU9yghu~y5#}q#Rk-C1JUrDZRO_P^DBE$NFUJ#R zZY$~Q>k!A?=F=`5cp~{Vbw@}+sTjF!qBD}ScmS1X>vCJ$LYg9ydjYO-#C-X8AxLFa ztFXo*ULbk-5a&NYNG2)zhM0C0J(2fN zhILHW;qAjBKq8m^i4;4KC1~U7zk1y6qPy64GkxO%Qd2ay&dHb_26krrflc=$>UCWa2#7eQ5CHXhMAcoIQXYi(vVOO-)CO|EC2F2$$?(NOUB3nc=QT-meYM64w-8D-6k8mRqI^mDE{ZC?hxA;ci zXV-5HK^j)`_t$an!dh7GxsJX_7l@bf@)j*Pa=Ko*5M)(x{!!JFzYvaU2XUaExFtp( z2%Z5t=#fDAJiaWSZ;vi{(0Utp_t#}?rYO?UmKLsJ-l=SgeJjw~xKmE5`j-H8>5#wc zbc<^4O3xu3v;VCF{mRS{>Ek75WRZ4k>3WVQ5`h?HQ)kFDH8hv#46TJJ&!atR@xh3y zWK#Ch#m^;h5;@F8FyEQZBY9E%_ZyOl{EVBz1wkiPs7k5V#CzdYCSF~GYF1#*i`NuM zQ4}gnw0&TU_~&yYys!rMR43D>#{KDds+*=%48UnxdNS>>QKPAfParPUMpcp;RvP{` zrZjFt5M$D~FeK{k&->bC;3yzj-7unr+UUco-wTE1oao-E=UFXWoyeMY!r>-&_I=@- z*rc(!LYdSo(oZUt_LQva`Pb(G+Dcj1UpSShW`V-w=jaV-IO=6C&1PVOFY(qqN$MQe zOh?K4wp82ffYxLHLPO2Gu4UwrH1|>uv{c%6n=q%a!$ao2E|cD@!9{s@eJMQmWO zsEXnPCau-KOdS&Istdk&_Y1M<#7e$YzCHU5B?U$P4d2HyBwNV?(^|K~?6c8U$<#H7 z;}=hRwoAl#zFGKb+H5N-3%ti!qHE(*BHW+$SBr4oAgU3J;`iV{m$lU2bzR--= z647can$xlwBQh7>*CTsZbW{G=xaPa)clStw-TnjO^IEh;&rjvpPUHUT@AR(vetL6! zBro{G0)b=A>Ie9nY>!mM+xNNuHY;S)Iv_bXYXD$)m1-0}#I7S}fZmkOA{R$~k!w<`$)dRa zBwUW3iV29gkW0%RXnJ)0Y$Nw&W+P$5+_t#f%_lS?e2tOoXu6?aQK*ufulSyK@$x46|h z;KwoUQ{m);8A2u^Q`=+B6Ou~}%(;&E7*UnqiZYpy~4;7nS ztMfC`!uHGL%j>e|zIboimNrQ3N{KPQ2!C8x@6~rv50x#px#J=20!P%rHlTLcpuK`J!kjS^+EA?uQymUrXg|GvBz%ZjZ z+B(MX=bv*GE5(cV2%4};->(X5q&OOe{i@M)iZy?)atSi`LypiE-eoG@z5&*6te3Yd**a2QN@f6}8sf(IKN_eH4Vip*+Xyi0C7XUl)hcUf0Jcns zVQV$|+?{}wEQtSIkL_f5(EcvgI%6sA&E=V>hppoM?itkQ<97aE*6%mS4NsRSm><}x z&*?4edtAnj+s?Q{Eaz3eB$IRUpZ$n|_{iz)zjheLliNqh8e=jYUmO}+xuO$lK?*~`g-k0*F zWOesbQU@$Pv(~JpiwOByvp?o+B!*-G+L)Z5gb!oBcL2F4PR& z-i`4O{$C94$gltw*!t|yo9N;BugJ{C12aSUiqrD8%0 z#l~)z=eBR;e1DRAVuF<;!m1k=xiYeM9`2SU;3)|17xKJq+I$FMPjut-b!^v(8My zT#4dC2jjjo8W+lLKP;>y4__+y!rbDnQo5si)GD4YohTyl5casyk0-BdPo8f6w{#K@ z#``g|Wz_o=%(d-9-%t*lPw|Un|jdE zY+1pqo8srCaPfrUaY6*?Wrt9Q*foaQy*B6k0IK9pQC z>>{4ROy*@aR4f~_zG|ipNGSN+Cv)Eh_|5_)pdqy2WWjkdc|o&<7%r7dcxZG5>hOBt zc@EpdMwAv+|0dsbp4-H2DV%Rbu8~YzB^6oofuc6*^IS?`p_(yh6S|SXU7yqM`~%R# zQd*ydO^{-YW@)Kgm~dy5t&~Vd^lWX$%)$eI{|YAgt!54nDHa9RM6l_-*OInyfKLz^ zoUAYhlem(fGRwyJJHPA+U6D!KQ@Y;i-G!@qGKuUiBw4T|Ah5$nU^yVDp}>WP*mKe! z-5-@@TSRH*q8&MkR_| zuBCDUNZ?$3boH-q6&>AmpO?&T;k6FThNzz;4+27GZ&X^EO=$`+YXtLAI2+|~RuZBj zgvHWn4iLPwWSO0pUKXKThFj1e;RRNDXMBUYFk^&pjYFk{+{l0hae z^vU@^I&LedV4!<$Ji<^4_90+4qtyN(IAWJFiQE`KIVwVhmi(8o43F%QHrY6i0fayY>DJFq$T|0!LoKn8Zq!)?X#W6 zymtT>v+>ER@)Ai3j2z345AMchoP40B@jO*R5%XKT%n;EANROL{ew%-Hav}{qKnFkM zkSOFAPfD9z=JB7$*6Z?cw6hLH^x(9_n{QWP-SCj_r$Z6JG!H!C^7CEOstkef>TY;qd*RSGQ#>CfoYTRJ{Rt67|t4ac}$$CAc(+;SV9LM3QDCfyO*Z zHU>uZncT}mFMS@WLlJG@S{N6WRCP~At7A|t?q|2?omz$to*m>nsP;#_#xc+)p&@Q% zOC~1D`Jv8v#$P5k867WFVAbC07D5ix-52`QFf4aDyw?MuB2^%10&x3xddiI&fO>Lr zWdpU5UNdT7j@=DL5$U&g`H#zq-V}bAsQF8{7iImszlZ`O0q1M~^p=#cNyp_}55dkT zT9Y5AKJ#}s{YT@Z1LK`|=!ep)xiMjer;=Cn$FdFga(e)V9uIm|QG9nXFVh}1avcPTi$iLdEC;!ka1vHQG z^lb|W5oujcUtUmQqtj;P4N*DYRo_5-gBB;s{{glN``?c11Bd?%j{(+9L8-lO#57)r z013G|dwgBPMnce6G@G8!YkPiyBWfl=YuCx{3jbC}$?tyna%7hW@e6tIPDjF2Gf$HF1TQ!S^LS%wF}FU0~Yu77e~_*`#sL?kJp zz3ulX0-(#|PaiH^UQ;ml^|E<%^^;YZ=5{-BDOeF_Rp)o5m(Ob6o&>j7fvr)Y#!FT|+S%KLHlf7B!yXmt09!v}vwM{=M=+lDxQvr1 zRZ6sO>!ZXBxre`}*17gB3%8yhYse9JsH~=Mih7(`R?d1_HW5UUdq{hE4y*e9&gkxBSCvLR+57g$(kz0GZ68s>Lr0cO!hrty}OHe;9Sz)=T*J+4#-gteBX z|5Yhxib{N;M96NNAslXbNBN<|t{GW7LY35~wp#jtQ2p$lUWd4+ zvvOTF*MbE)LmK-_d!g-cB5wY(#&_eaS7XMgEAp%E0LC+I-oUj0{V3C5r>(jFH^6%( zB!ZX+*be!s{M)C$WJ|B;o5NWWI&=`4@=p^QLNW-ZIult1}KR6{l{{zy;$ z^9R~aPI*GWaXK1%(!B@FIDIn60pnb`ZJ%ARIV{KlqQb^N7c#q$Ww4%gme9;2d?+0g z-FB(?XrEz_8T{C|nP=7{LVH)fzDunzzLVd8BL~KbLSF=aiwf#t!s2xmp}yn#pF_qm zqY9>1R6G<+zCKVKe|-J1yXCl{VbC`T1mX%noE<`LK~BJNKcwSKCty7{9AAd3xqNu` zqjIX&_0;r-hLTpzu% z3L`muaaVx3fXDb7hD1>F*S<*{3c5G`AljFcFz(MuGG<&|NDo)_>}00NbusK>v~z%6?10tfy-3^I?_hi-S3 zI|OE|WDO6=ji;hNR|VCz56VQMyF%okkD3aAFDE?`x{?B*ucPg5xQb}~UDMKXYaha< zijt>F!Ka})IhMuDO&|3=@%%k)rXs=TnVW`^d=zJZ1-oh13+)<{z%Nt7<#7Kh>br4PLW+^0;xS2Bj;cp#PEgq5AOiA8~Uh z)D7C4-vF0RBuVwaNjC7u#z2Zt#57#jOm}|01}Zx#6$=0K*3;)l&|wbN^MB~KNOYS8 z*srG4QtROPrYz_K)sr1F+Azib__41itv;MBzxn-exa%&Gz2*PAN+KQg7ev{C!#|+Z zH!hfuem?d49bSTOgAI0{S98)$Z5Km8Mq1FDCdhD;d+mL-7dYL&4+g>o$0;SH>yg7a zEGHtba3Ne_s}u+k@1j%j|1RvgH(H#tl{LJ{Xbn;{Pgzfrl-_-CL#^ArUE-EHWM2Q> z7cODM$T=zkZHRrHF&L`4`vo`U$ECDgU?z#Ov=r0i#v8l88rv)Z{uF@D_Ld4f*nNuB zlHkcgduip@)CSRE)b3_WZ)%nh$p8J%OK{}!9z2HwKVDn;-$MU=`rq2S%l`j&{~rYZ f?^JL|{_ky>0lUveB8IwqfS044i!IaULc;$64nA|= literal 0 HcmV?d00001 diff --git a/app/src/assets/image/localAssets/arch.png b/app/src/assets/image/localAssets/arch.png deleted file mode 100644 index bf1427839dd9ba4b62d054df0eab3070b03367ec..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 40000 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 diff --git a/app/src/assets/image/localAssets/window.png b/app/src/assets/image/localAssets/window.png deleted file mode 100644 index c6dbd54ff94b2a770f74dcf9923a0c9223ee4f50..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 80000 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 z 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 S0YCr{00aO5K;Zwk!2bc7VGbDp diff --git a/app/src/assets/image/sampleDecal.png b/app/src/assets/image/sampleDecal.png deleted file mode 100644 index ed8c9fd9524d841f1c55facac183f081857a7931..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 35553 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 Q4nOu28>GYKT1y}Ne;xdo1ONa4 diff --git a/app/src/modules/scene/controls/selectionControls/selection3D/boundingBoxHelper3D.tsx b/app/src/modules/scene/controls/selectionControls/selection3D/boundingBoxHelper3D.tsx index aab6856..b6b49ef 100644 --- a/app/src/modules/scene/controls/selectionControls/selection3D/boundingBoxHelper3D.tsx +++ b/app/src/modules/scene/controls/selectionControls/selection3D/boundingBoxHelper3D.tsx @@ -88,9 +88,9 @@ const BoundingBox = ({ boundingBoxRef, isPerAsset = true }: BoundingBoxProps) => > { + const savedTheme = localStorage.getItem("theme"); + const isLogedIn = localStorage.getItem("userId"); + const navigate = useNavigate(); + const { hash } = useLocation(); + + function getErrorContext() { + const contexts = hash.split("#"); + const context = contexts[1]; + const info = contexts.length > 1 ? contexts[2] : ""; + switch (context) { + case "project_not_found": + return `Project Not found - The project ${ + info !== "" && `with ID (${info})` + } was not found.`; + default: + return "Page not Found - looks like we have hit a roadblock"; + } + } + + return ( +

+ ); +}; + +export default PageNotFound; diff --git a/app/src/pages/Project.tsx b/app/src/pages/Project.tsx index 20aad02..d7bebd8 100644 --- a/app/src/pages/Project.tsx +++ b/app/src/pages/Project.tsx @@ -42,6 +42,7 @@ const Project: React.FC = () => { useEffect(() => { if (!email || !userId) { console.error("User data not found in localStorage"); + navigate("/page-not-found"); return; } @@ -60,6 +61,7 @@ const Project: React.FC = () => { await viewProject(organization, matchedProject._id, userId); } else { console.warn("Project not found with given ID:", projectId); + navigate(`/not_found#project_not_found#${projectId}`); } } catch (error) { console.error("Error fetching projects:", error); @@ -72,6 +74,7 @@ const Project: React.FC = () => { useEffect(() => { if (!projectId) return; + getVersionHistoryApi(projectId) .then((data) => { const versions: VersionHistory = []; diff --git a/app/src/pages/UserAuth.tsx b/app/src/pages/UserAuth.tsx index 06352a0..4db2358 100644 --- a/app/src/pages/UserAuth.tsx +++ b/app/src/pages/UserAuth.tsx @@ -36,8 +36,6 @@ const UserAuth: React.FC = () => { initializeFingerprint(); }, []); - const { userId, organization } = getUserData(); - const handleLogin = async (e: FormEvent) => { e.preventDefault(); const organization = email.split("@")[1].split(".")[0]; diff --git a/app/src/styles/main.scss b/app/src/styles/main.scss index cc59a33..6da5352 100644 --- a/app/src/styles/main.scss +++ b/app/src/styles/main.scss @@ -45,6 +45,7 @@ @use "pages/realTimeViz"; @use "pages/userAuth"; @use "pages/forgotPassword"; +@use "pages/pageNotFound.scss"; // @use "./scene/scene"; diff --git a/app/src/styles/pages/pageNotFound.scss b/app/src/styles/pages/pageNotFound.scss new file mode 100644 index 0000000..f185f47 --- /dev/null +++ b/app/src/styles/pages/pageNotFound.scss @@ -0,0 +1,56 @@ +@use "../abstracts/variables" as *; +@use "../abstracts/mixins" as *; + +.page-not-found-wrapper { + height: 100vh; + width: 100vw; + background: var(--background-color); + .page-not-found-container { + height: 100%; + width: 100%; + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; + gap: 30px; + &::after { + content: ""; + position: absolute; + background: var(--faint-gradient-color); + height: 100vh; + width: 100vw; + top: 0; + left: 0; + z-index: -1; + } + .text-404 { + height: 10vh; + img { + height: 100%; + width: 100%; + } + } + .hero-container { + height: 50vh; + img { + height: 100%; + width: 100%; + } + } + .context { + color: var(--text-color); + font-size: 1rem; + } + .back-to-home { + background: var(--background-color-button); + color: var(--text-button-color); + padding: 8px 16px; + border-radius: 30px; + cursor: pointer; + transition: all 0.2s; + &:hover { + transform: translateY(-2px); + } + } + } +} From 62ddc1c25fe78d8e8d9fce4b48a5c6c48a404e9f Mon Sep 17 00:00:00 2001 From: Vishnu Date: Fri, 29 Aug 2025 16:26:49 +0530 Subject: [PATCH 29/34] styles file name updated --- app/src/styles/abstracts/{functions.scss => _functions.scss} | 0 app/src/styles/abstracts/{mixins.scss => _mixins.scss} | 0 app/src/styles/abstracts/{variables.scss => _variables.scss} | 0 app/src/styles/base/{base.scss => _base.scss} | 0 app/src/styles/base/{global.scss => _global.scss} | 0 app/src/styles/base/{reset.scss => _reset.scss} | 0 app/src/styles/base/{typography.scss => _typography.scss} | 0 app/src/styles/components/{button.scss => _button.scss} | 0 .../{confirmationPopUp.scss => _confirmationPopUp.scss} | 0 app/src/styles/components/{form.scss => _form.scss} | 0 app/src/styles/components/{input.scss => _input.scss} | 0 app/src/styles/components/{lists.scss => _lists.scss} | 0 .../styles/components/{moduleToggle.scss => _moduleToggle.scss} | 0 app/src/styles/components/{templates.scss => _templates.scss} | 0 app/src/styles/components/{tools.scss => _tools.scss} | 0 app/src/styles/components/footer/{footer.scss => _footer.scss} | 0 app/src/styles/components/logs/{logs.scss => _logs.scss} | 0 .../marketPlace/{marketPlace.scss => _marketPlace.scss} | 0 app/src/styles/components/menu/{menu.scss => _menu.scss} | 0 .../components/simulation/{analysis.scss => _analysis.scss} | 0 .../components/simulation/{simulation.scss => _simulation.scss} | 0 .../visualization/floating/{common.scss => _common.scss} | 0 .../floating/{energyConsumed.scss => _energyConsumed.scss} | 0 .../visualization/ui/{styledWidgets.scss => _styledWidgets.scss} | 0 app/src/styles/layout/{compareLayout.scss => _compareLayout.scss} | 0 .../layout/{compareLayoutPopUp.scss => _compareLayoutPopUp.scss} | 0 app/src/styles/layout/{loading.scss => _loading.scss} | 0 app/src/styles/layout/{popup.scss => _popup.scss} | 0 .../layout/{resourceManagement.scss => _resourceManagement.scss} | 0 app/src/styles/layout/{sidebar.scss => _sidebar.scss} | 0 app/src/styles/layout/{skeleton.scss => _skeleton.scss} | 0 app/src/styles/layout/{toast.scss => _toast.scss} | 0 app/src/styles/pages/{dashboard.scss => _dashboard.scss} | 0 .../styles/pages/{forgotPassword.scss => _forgotPassword.scss} | 0 app/src/styles/pages/{home.scss => _home.scss} | 0 app/src/styles/pages/{pageNotFound.scss => _pageNotFound.scss} | 0 app/src/styles/pages/{realTimeViz.scss => _realTimeViz.scss} | 0 app/src/styles/pages/{userAuth.scss => _userAuth.scss} | 0 app/src/styles/scene/{comments.scss => _comments.scss} | 0 app/src/styles/scene/{cursors.scss => _cursors.scss} | 0 app/src/styles/scene/{scene.scss => _scene.scss} | 0 41 files changed, 0 insertions(+), 0 deletions(-) rename app/src/styles/abstracts/{functions.scss => _functions.scss} (100%) rename app/src/styles/abstracts/{mixins.scss => _mixins.scss} (100%) rename app/src/styles/abstracts/{variables.scss => _variables.scss} (100%) rename app/src/styles/base/{base.scss => _base.scss} (100%) rename app/src/styles/base/{global.scss => _global.scss} (100%) rename app/src/styles/base/{reset.scss => _reset.scss} (100%) rename app/src/styles/base/{typography.scss => _typography.scss} (100%) rename app/src/styles/components/{button.scss => _button.scss} (100%) rename app/src/styles/components/{confirmationPopUp.scss => _confirmationPopUp.scss} (100%) rename app/src/styles/components/{form.scss => _form.scss} (100%) rename app/src/styles/components/{input.scss => _input.scss} (100%) rename app/src/styles/components/{lists.scss => _lists.scss} (100%) rename app/src/styles/components/{moduleToggle.scss => _moduleToggle.scss} (100%) rename app/src/styles/components/{templates.scss => _templates.scss} (100%) rename app/src/styles/components/{tools.scss => _tools.scss} (100%) rename app/src/styles/components/footer/{footer.scss => _footer.scss} (100%) rename app/src/styles/components/logs/{logs.scss => _logs.scss} (100%) rename app/src/styles/components/marketPlace/{marketPlace.scss => _marketPlace.scss} (100%) rename app/src/styles/components/menu/{menu.scss => _menu.scss} (100%) rename app/src/styles/components/simulation/{analysis.scss => _analysis.scss} (100%) rename app/src/styles/components/simulation/{simulation.scss => _simulation.scss} (100%) rename app/src/styles/components/visualization/floating/{common.scss => _common.scss} (100%) rename app/src/styles/components/visualization/floating/{energyConsumed.scss => _energyConsumed.scss} (100%) rename app/src/styles/components/visualization/ui/{styledWidgets.scss => _styledWidgets.scss} (100%) rename app/src/styles/layout/{compareLayout.scss => _compareLayout.scss} (100%) rename app/src/styles/layout/{compareLayoutPopUp.scss => _compareLayoutPopUp.scss} (100%) rename app/src/styles/layout/{loading.scss => _loading.scss} (100%) rename app/src/styles/layout/{popup.scss => _popup.scss} (100%) rename app/src/styles/layout/{resourceManagement.scss => _resourceManagement.scss} (100%) rename app/src/styles/layout/{sidebar.scss => _sidebar.scss} (100%) rename app/src/styles/layout/{skeleton.scss => _skeleton.scss} (100%) rename app/src/styles/layout/{toast.scss => _toast.scss} (100%) rename app/src/styles/pages/{dashboard.scss => _dashboard.scss} (100%) rename app/src/styles/pages/{forgotPassword.scss => _forgotPassword.scss} (100%) rename app/src/styles/pages/{home.scss => _home.scss} (100%) rename app/src/styles/pages/{pageNotFound.scss => _pageNotFound.scss} (100%) rename app/src/styles/pages/{realTimeViz.scss => _realTimeViz.scss} (100%) rename app/src/styles/pages/{userAuth.scss => _userAuth.scss} (100%) rename app/src/styles/scene/{comments.scss => _comments.scss} (100%) rename app/src/styles/scene/{cursors.scss => _cursors.scss} (100%) rename app/src/styles/scene/{scene.scss => _scene.scss} (100%) diff --git a/app/src/styles/abstracts/functions.scss b/app/src/styles/abstracts/_functions.scss similarity index 100% rename from app/src/styles/abstracts/functions.scss rename to app/src/styles/abstracts/_functions.scss diff --git a/app/src/styles/abstracts/mixins.scss b/app/src/styles/abstracts/_mixins.scss similarity index 100% rename from app/src/styles/abstracts/mixins.scss rename to app/src/styles/abstracts/_mixins.scss diff --git a/app/src/styles/abstracts/variables.scss b/app/src/styles/abstracts/_variables.scss similarity index 100% rename from app/src/styles/abstracts/variables.scss rename to app/src/styles/abstracts/_variables.scss diff --git a/app/src/styles/base/base.scss b/app/src/styles/base/_base.scss similarity index 100% rename from app/src/styles/base/base.scss rename to app/src/styles/base/_base.scss diff --git a/app/src/styles/base/global.scss b/app/src/styles/base/_global.scss similarity index 100% rename from app/src/styles/base/global.scss rename to app/src/styles/base/_global.scss diff --git a/app/src/styles/base/reset.scss b/app/src/styles/base/_reset.scss similarity index 100% rename from app/src/styles/base/reset.scss rename to app/src/styles/base/_reset.scss diff --git a/app/src/styles/base/typography.scss b/app/src/styles/base/_typography.scss similarity index 100% rename from app/src/styles/base/typography.scss rename to app/src/styles/base/_typography.scss diff --git a/app/src/styles/components/button.scss b/app/src/styles/components/_button.scss similarity index 100% rename from app/src/styles/components/button.scss rename to app/src/styles/components/_button.scss diff --git a/app/src/styles/components/confirmationPopUp.scss b/app/src/styles/components/_confirmationPopUp.scss similarity index 100% rename from app/src/styles/components/confirmationPopUp.scss rename to app/src/styles/components/_confirmationPopUp.scss diff --git a/app/src/styles/components/form.scss b/app/src/styles/components/_form.scss similarity index 100% rename from app/src/styles/components/form.scss rename to app/src/styles/components/_form.scss diff --git a/app/src/styles/components/input.scss b/app/src/styles/components/_input.scss similarity index 100% rename from app/src/styles/components/input.scss rename to app/src/styles/components/_input.scss diff --git a/app/src/styles/components/lists.scss b/app/src/styles/components/_lists.scss similarity index 100% rename from app/src/styles/components/lists.scss rename to app/src/styles/components/_lists.scss diff --git a/app/src/styles/components/moduleToggle.scss b/app/src/styles/components/_moduleToggle.scss similarity index 100% rename from app/src/styles/components/moduleToggle.scss rename to app/src/styles/components/_moduleToggle.scss diff --git a/app/src/styles/components/templates.scss b/app/src/styles/components/_templates.scss similarity index 100% rename from app/src/styles/components/templates.scss rename to app/src/styles/components/_templates.scss diff --git a/app/src/styles/components/tools.scss b/app/src/styles/components/_tools.scss similarity index 100% rename from app/src/styles/components/tools.scss rename to app/src/styles/components/_tools.scss diff --git a/app/src/styles/components/footer/footer.scss b/app/src/styles/components/footer/_footer.scss similarity index 100% rename from app/src/styles/components/footer/footer.scss rename to app/src/styles/components/footer/_footer.scss diff --git a/app/src/styles/components/logs/logs.scss b/app/src/styles/components/logs/_logs.scss similarity index 100% rename from app/src/styles/components/logs/logs.scss rename to app/src/styles/components/logs/_logs.scss diff --git a/app/src/styles/components/marketPlace/marketPlace.scss b/app/src/styles/components/marketPlace/_marketPlace.scss similarity index 100% rename from app/src/styles/components/marketPlace/marketPlace.scss rename to app/src/styles/components/marketPlace/_marketPlace.scss diff --git a/app/src/styles/components/menu/menu.scss b/app/src/styles/components/menu/_menu.scss similarity index 100% rename from app/src/styles/components/menu/menu.scss rename to app/src/styles/components/menu/_menu.scss diff --git a/app/src/styles/components/simulation/analysis.scss b/app/src/styles/components/simulation/_analysis.scss similarity index 100% rename from app/src/styles/components/simulation/analysis.scss rename to app/src/styles/components/simulation/_analysis.scss diff --git a/app/src/styles/components/simulation/simulation.scss b/app/src/styles/components/simulation/_simulation.scss similarity index 100% rename from app/src/styles/components/simulation/simulation.scss rename to app/src/styles/components/simulation/_simulation.scss diff --git a/app/src/styles/components/visualization/floating/common.scss b/app/src/styles/components/visualization/floating/_common.scss similarity index 100% rename from app/src/styles/components/visualization/floating/common.scss rename to app/src/styles/components/visualization/floating/_common.scss diff --git a/app/src/styles/components/visualization/floating/energyConsumed.scss b/app/src/styles/components/visualization/floating/_energyConsumed.scss similarity index 100% rename from app/src/styles/components/visualization/floating/energyConsumed.scss rename to app/src/styles/components/visualization/floating/_energyConsumed.scss diff --git a/app/src/styles/components/visualization/ui/styledWidgets.scss b/app/src/styles/components/visualization/ui/_styledWidgets.scss similarity index 100% rename from app/src/styles/components/visualization/ui/styledWidgets.scss rename to app/src/styles/components/visualization/ui/_styledWidgets.scss diff --git a/app/src/styles/layout/compareLayout.scss b/app/src/styles/layout/_compareLayout.scss similarity index 100% rename from app/src/styles/layout/compareLayout.scss rename to app/src/styles/layout/_compareLayout.scss diff --git a/app/src/styles/layout/compareLayoutPopUp.scss b/app/src/styles/layout/_compareLayoutPopUp.scss similarity index 100% rename from app/src/styles/layout/compareLayoutPopUp.scss rename to app/src/styles/layout/_compareLayoutPopUp.scss diff --git a/app/src/styles/layout/loading.scss b/app/src/styles/layout/_loading.scss similarity index 100% rename from app/src/styles/layout/loading.scss rename to app/src/styles/layout/_loading.scss diff --git a/app/src/styles/layout/popup.scss b/app/src/styles/layout/_popup.scss similarity index 100% rename from app/src/styles/layout/popup.scss rename to app/src/styles/layout/_popup.scss diff --git a/app/src/styles/layout/resourceManagement.scss b/app/src/styles/layout/_resourceManagement.scss similarity index 100% rename from app/src/styles/layout/resourceManagement.scss rename to app/src/styles/layout/_resourceManagement.scss diff --git a/app/src/styles/layout/sidebar.scss b/app/src/styles/layout/_sidebar.scss similarity index 100% rename from app/src/styles/layout/sidebar.scss rename to app/src/styles/layout/_sidebar.scss diff --git a/app/src/styles/layout/skeleton.scss b/app/src/styles/layout/_skeleton.scss similarity index 100% rename from app/src/styles/layout/skeleton.scss rename to app/src/styles/layout/_skeleton.scss diff --git a/app/src/styles/layout/toast.scss b/app/src/styles/layout/_toast.scss similarity index 100% rename from app/src/styles/layout/toast.scss rename to app/src/styles/layout/_toast.scss diff --git a/app/src/styles/pages/dashboard.scss b/app/src/styles/pages/_dashboard.scss similarity index 100% rename from app/src/styles/pages/dashboard.scss rename to app/src/styles/pages/_dashboard.scss diff --git a/app/src/styles/pages/forgotPassword.scss b/app/src/styles/pages/_forgotPassword.scss similarity index 100% rename from app/src/styles/pages/forgotPassword.scss rename to app/src/styles/pages/_forgotPassword.scss diff --git a/app/src/styles/pages/home.scss b/app/src/styles/pages/_home.scss similarity index 100% rename from app/src/styles/pages/home.scss rename to app/src/styles/pages/_home.scss diff --git a/app/src/styles/pages/pageNotFound.scss b/app/src/styles/pages/_pageNotFound.scss similarity index 100% rename from app/src/styles/pages/pageNotFound.scss rename to app/src/styles/pages/_pageNotFound.scss diff --git a/app/src/styles/pages/realTimeViz.scss b/app/src/styles/pages/_realTimeViz.scss similarity index 100% rename from app/src/styles/pages/realTimeViz.scss rename to app/src/styles/pages/_realTimeViz.scss diff --git a/app/src/styles/pages/userAuth.scss b/app/src/styles/pages/_userAuth.scss similarity index 100% rename from app/src/styles/pages/userAuth.scss rename to app/src/styles/pages/_userAuth.scss diff --git a/app/src/styles/scene/comments.scss b/app/src/styles/scene/_comments.scss similarity index 100% rename from app/src/styles/scene/comments.scss rename to app/src/styles/scene/_comments.scss diff --git a/app/src/styles/scene/cursors.scss b/app/src/styles/scene/_cursors.scss similarity index 100% rename from app/src/styles/scene/cursors.scss rename to app/src/styles/scene/_cursors.scss diff --git a/app/src/styles/scene/scene.scss b/app/src/styles/scene/_scene.scss similarity index 100% rename from app/src/styles/scene/scene.scss rename to app/src/styles/scene/_scene.scss From ef9c3a9c6312b7d5618a03d4746d4e195c38eecb Mon Sep 17 00:00:00 2001 From: Vishnu Date: Fri, 29 Aug 2025 16:39:14 +0530 Subject: [PATCH 30/34] dashboard sidebar updated --- app/src/components/Dashboard/SidePannel.tsx | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/app/src/components/Dashboard/SidePannel.tsx b/app/src/components/Dashboard/SidePannel.tsx index db7d203..2c0ad64 100644 --- a/app/src/components/Dashboard/SidePannel.tsx +++ b/app/src/components/Dashboard/SidePannel.tsx @@ -64,7 +64,7 @@ const SidePannel: React.FC = ({ setActiveTab, activeTab }) => { if (projectSocket) { const handleResponse = (data: any) => { if (data.message === "Project created successfully") { - setLoadingProgress(1) + setLoadingProgress(1); navigate(`/projects/${data.data.projectId}`); } projectSocket.off("v1-project:response:add", handleResponse); // Clean up @@ -141,7 +141,10 @@ const SidePannel: React.FC = ({ setActiveTab, activeTab }) => { activeTab === "Tutorials" ? "option-list active" : "option-list" } title="coming soon" - onClick={() => setActiveTab("Tutorials")} + onClick={() => { + // setActiveTab("Tutorials"); + console.warn("Tutorials comming soon"); + }} > Tutorials @@ -153,14 +156,17 @@ const SidePannel: React.FC = ({ setActiveTab, activeTab }) => { : "option-list" } title="coming soon" - onClick={() => setActiveTab("Documentation")} + onClick={() => { + // setActiveTab("Documentation"); + console.warn("Documentation comming soon"); + }} > Documentation
-
-
+
+
Settings
@@ -175,7 +181,7 @@ const SidePannel: React.FC = ({ setActiveTab, activeTab }) => { Log out
-
+
Help & Feedback
From b956ed57e8f7a70b67ec2d6908cc8446e49bffd3 Mon Sep 17 00:00:00 2001 From: Vishnu Date: Fri, 29 Aug 2025 17:07:00 +0530 Subject: [PATCH 31/34] human resource management profile ui fix --- .../resourceManagement/hrm/Hrm.tsx | 456 ++++++++++-------- .../styles/layout/_resourceManagement.scss | 21 +- 2 files changed, 253 insertions(+), 224 deletions(-) diff --git a/app/src/components/layout/sidebarRight/resourceManagement/hrm/Hrm.tsx b/app/src/components/layout/sidebarRight/resourceManagement/hrm/Hrm.tsx index efb5cf3..ce17d60 100644 --- a/app/src/components/layout/sidebarRight/resourceManagement/hrm/Hrm.tsx +++ b/app/src/components/layout/sidebarRight/resourceManagement/hrm/Hrm.tsx @@ -1,200 +1,225 @@ -import { useEffect, useState } from 'react' -import { ClockThreeIcon, LocationPinIcon, TargetIcon } from '../../../../icons/ExportCommonIcons' -import { useSceneContext } from '../../../../../modules/scene/sceneContext'; -import RenameInput from '../../../../ui/inputs/RenameInput'; -import { useResourceManagementId } from '../../../../../store/builder/store'; -import { getAssetThumbnail } from '../../../../../services/factoryBuilder/asset/assets/getAssetThumbnail'; +import { useEffect, useState } from "react"; +import { + ClockThreeIcon, + LocationPinIcon, + TargetIcon, +} from "../../../../icons/ExportCommonIcons"; +import { useSceneContext } from "../../../../../modules/scene/sceneContext"; +import RenameInput from "../../../../ui/inputs/RenameInput"; +import { useResourceManagementId } from "../../../../../store/builder/store"; +import { getAssetThumbnail } from "../../../../../services/factoryBuilder/asset/assets/getAssetThumbnail"; // import NavigateCatagory from '../NavigateCatagory' const Hrm = () => { - const [selectedCard, setSelectedCard] = useState(0); - const [workers, setWorkers] = useState([]); - const { setResourceManagementId } = useResourceManagementId(); - const { assetStore } = useSceneContext(); - const { assets: allAssets } = assetStore(); + const [selectedCard, setSelectedCard] = useState(0); + const [workers, setWorkers] = useState([]); + const { setResourceManagementId } = useResourceManagementId(); + const { assetStore } = useSceneContext(); + const { assets: allAssets } = assetStore(); - async function getAsset(assetId: string) { - let thumbnail = await getAssetThumbnail(assetId) - if (thumbnail.thumbnail) { - let assetImage = thumbnail.thumbnail - return assetImage; - } + async function getAsset(assetId: string) { + let thumbnail = await getAssetThumbnail(assetId); + if (thumbnail.thumbnail) { + let assetImage = thumbnail.thumbnail; + return assetImage; } + } - useEffect(() => { + useEffect(() => { if (allAssets.length > 0) { - const fetchWorkers = async () => { - const humans = allAssets.filter((worker: any) => worker.eventData.type === "Human"); + const fetchWorkers = async () => { + const humans = allAssets.filter( + (worker: any) => worker.eventData.type === "Human" + ); - const formattedWorkers = await Promise.all( - humans.map(async (worker: any, index: number) => { - const assetImage = await getAsset(worker.assetId); + const formattedWorkers = await Promise.all( + humans.map(async (worker: any, index: number) => { + const assetImage = await getAsset(worker.assetId); - return { - employee: { - image: assetImage, - name: worker.modelName, - modelId: worker.modelUuid, - employee_id: `HR-${204 + index}`, - status: "Active", - }, - task: { - status: "Ongoing", - title: worker.taskTitle ?? "No Task Assigned", - location: { - floor: worker.floor ?? 0, - zone: worker.zone ?? "N/A", - }, - planned_time_hours: worker.plannedTime ?? 0, - time_spent_hours: worker.timeSpent ?? 0, - total_tasks: worker.totalTasks ?? 0, - completed_tasks: worker.completedTasks ?? 0, - }, - actions: ["Assign Task", "Reassign Task", "Pause", "Emergency Stop"], - location: `Floor ${worker.floor || "-"} . Zone ${worker.zone || "-"}`, - }; - }) - ); + return { + employee: { + image: assetImage, + name: worker.modelName, + modelId: worker.modelUuid, + employee_id: `HR-${204 + index}`, + status: "Active", + }, + task: { + status: "Ongoing", + title: worker.taskTitle ?? "No Task Assigned", + location: { + floor: worker.floor ?? 0, + zone: worker.zone ?? "N/A", + }, + planned_time_hours: worker.plannedTime ?? 0, + time_spent_hours: worker.timeSpent ?? 0, + total_tasks: worker.totalTasks ?? 0, + completed_tasks: worker.completedTasks ?? 0, + }, + actions: [ + "Assign Task", + "Reassign Task", + "Pause", + "Emergency Stop", + ], + location: `Floor ${worker.floor || "-"} . Zone ${ + worker.zone || "-" + }`, + }; + }) + ); - setWorkers(formattedWorkers); - }; + setWorkers(formattedWorkers); + }; - fetchWorkers(); + fetchWorkers(); } -}, [allAssets]); + }, [allAssets]); + // const employee_details = [ + // { + // "employee": { + // image: "", + // "name": "John Doe", + // "employee_id": "HR-204", + // "status": "Active", + // }, + // "task": { + // "status": "Ongoing", + // "title": "Inspecting Machine X", + // "location": { + // "floor": 4, + // "zone": "B" + // }, + // "planned_time_hours": 6, + // "time_spent_hours": 2, + // "total_tasks": 12, + // "completed_tasks": 3 + // }, + // "actions": [ + // "Assign Task", + // "Reassign Task", + // "Pause", + // "Emergency Stop" + // ], + // "location": "Floor 4 . Zone B" + // }, + // { + // "employee": { + // image: "", + // "name": "Alice Smith", + // "employee_id": "HR-205", + // "status": "Active", -// const employee_details = [ -// { -// "employee": { -// image: "", -// "name": "John Doe", -// "employee_id": "HR-204", -// "status": "Active", + // }, + // "task": { + // "status": "Ongoing", + // "title": "Calibrating Sensor Y", + // "location": { + // "floor": 2, + // "zone": "A" + // }, + // "planned_time_hours": 4, + // "time_spent_hours": 1.5, + // "total_tasks": 10, + // "completed_tasks": 2 + // }, + // "actions": [ + // "Assign Task", + // "Reassign Task", + // "Pause", + // "Emergency Stop" + // ], + // "location": "Floor 4 . Zone B" + // }, + // { + // "employee": { + // image: "", + // "name": "Michael Lee", + // "employee_id": "HR-206", + // "status": "Active", -// }, -// "task": { -// "status": "Ongoing", -// "title": "Inspecting Machine X", -// "location": { -// "floor": 4, -// "zone": "B" -// }, -// "planned_time_hours": 6, -// "time_spent_hours": 2, -// "total_tasks": 12, -// "completed_tasks": 3 -// }, -// "actions": [ -// "Assign Task", -// "Reassign Task", -// "Pause", -// "Emergency Stop" -// ], -// "location": "Floor 4 . Zone B" -// }, -// { -// "employee": { -// image: "", -// "name": "Alice Smith", -// "employee_id": "HR-205", -// "status": "Active", - -// }, -// "task": { -// "status": "Ongoing", -// "title": "Calibrating Sensor Y", -// "location": { -// "floor": 2, -// "zone": "A" -// }, -// "planned_time_hours": 4, -// "time_spent_hours": 1.5, -// "total_tasks": 10, -// "completed_tasks": 2 -// }, -// "actions": [ -// "Assign Task", -// "Reassign Task", -// "Pause", -// "Emergency Stop" -// ], -// "location": "Floor 4 . Zone B" -// }, -// { -// "employee": { -// image: "", -// "name": "Michael Lee", -// "employee_id": "HR-206", -// "status": "Active", - -// }, -// "task": { -// "status": "Ongoing", -// "title": "Testing Conveyor Belt Z", -// "location": { -// "floor": 5, -// "zone": "C" -// }, -// "planned_time_hours": 5, -// "time_spent_hours": 3, -// "total_tasks": 8, -// "completed_tasks": 5 -// }, -// "actions": [ -// "Assign Task", -// "Reassign Task", -// "Pause", -// "Emergency Stop" -// ], -// "location": "Floor 4 . Zone B" -// }, -// ] -function handleRenameWorker(newName: string) { - - -} -function handleHumanClick(employee: any) { + // }, + // "task": { + // "status": "Ongoing", + // "title": "Testing Conveyor Belt Z", + // "location": { + // "floor": 5, + // "zone": "C" + // }, + // "planned_time_hours": 5, + // "time_spent_hours": 3, + // "total_tasks": 8, + // "completed_tasks": 5 + // }, + // "actions": [ + // "Assign Task", + // "Reassign Task", + // "Pause", + // "Emergency Stop" + // ], + // "location": "Floor 4 . Zone B" + // }, + // ] + function handleRenameWorker(newName: string) {} + function handleHumanClick(employee: any) { if (employee.modelId) { - setResourceManagementId(employee.modelId); + setResourceManagementId(employee.modelId); } -} + } - -return ( + return ( <> - {/* */} -
- {workers.map((employee, index) => ( -
setSelectedCard(index)} - key={index} - > -
-
-
- -
-
-
- {/*
{employee.employee.name}
*/} - -
{employee.employee.employee_id}
-
-
+
+ {workers.map((employee, index) => ( +
setSelectedCard(index)} + key={index} + > +
+
+
+ +
+
+
+ {/*
{employee.employee.name}
*/} + +
+ {employee.employee.employee_id} +
+
+
-
{ handleHumanClick(employee.employee) }}>View in Scene
-
+
{ + handleHumanClick(employee.employee); + }} + > + View in Scene +
+
-
- {/*
+
+ {/*
@@ -215,17 +240,20 @@ return (
*/} -
-
+
+
+
+ + + + Planned time: +
-
- - Planned time: -
- - {employee.task.planned_time_hours} hr -
- {/*
+ + {employee.task.planned_time_hours} hr + +
+ {/*
@@ -243,39 +271,41 @@ return ( {employee.task.time_spent_hours} hr
*/} -
- -
- - Cost per hr: -
- - {employee.task.completed_tasks} -
-
- -
-
-
- -
-
Location:
-
-
{employee.location}
-
-
- {/* - */} - - -
-
+
+
+ + + + Cost per hr: +
+ + {employee.task.completed_tasks} +
- ))} -
- -) -} +
-export default Hrm +
+
+
+ +
+
Location:
+
+
{employee.location}
+
+ {/*
+ + + + +
*/} +
+
+ ))} +
+ + ); +}; + +export default Hrm; diff --git a/app/src/styles/layout/_resourceManagement.scss b/app/src/styles/layout/_resourceManagement.scss index 9582a24..48d768c 100644 --- a/app/src/styles/layout/_resourceManagement.scss +++ b/app/src/styles/layout/_resourceManagement.scss @@ -114,35 +114,34 @@ .user-image-wrapper { width: 28px; height: 28px; - border-radius: 50%; - background-color: #fff; position: relative; - overflow: hidden; - .user-image{ - height: 300%; - width: 300%; - transform: translate(-26px, -12px); + .user-image { + height: 100%; + width: 100%; + border-radius: 50%; + background-color: #fff; + overflow: hidden; + // transform: translate(-26px, -12px); } .status { border-radius: 50%; width: 6px; height: 6px; - outline: 1px solid #2f2c32; - + outline: 2px solid var(--background-color-solid); position: absolute; bottom: 0; right: 0; &.Active { - background-color: #44e5c6; + background-color: #1d9419; } } } .details { max-width: 144px; - .input-value{ + .input-value { max-width: 120px; } .employee-id { From f9d314b69f5970f46fc00a1be53091c0c5b2740b Mon Sep 17 00:00:00 2001 From: Vishnu Date: Fri, 29 Aug 2025 17:16:50 +0530 Subject: [PATCH 32/34] decal default scale scale update --- app/src/modules/builder/Decal/decalCreator/decalCreator.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/modules/builder/Decal/decalCreator/decalCreator.tsx b/app/src/modules/builder/Decal/decalCreator/decalCreator.tsx index fe14974..44e46e3 100644 --- a/app/src/modules/builder/Decal/decalCreator/decalCreator.tsx +++ b/app/src/modules/builder/Decal/decalCreator/decalCreator.tsx @@ -55,7 +55,7 @@ function DecalCreator() { decalPosition: [point.x, point.y, (wall.wallThickness / 2 + 0.001) * (wallIntersect.normal?.z || 1)], decalRotation: 0, decalOpacity: 1, - decalScale: 1, + decalScale: 0.5, } addDecalOnWall(wallIntersect.object.userData.wallUuid, decal); From 3bc0e2826709d8bd8f1601da08fa301de995f327 Mon Sep 17 00:00:00 2001 From: Vishnu Date: Fri, 29 Aug 2025 17:47:29 +0530 Subject: [PATCH 33/34] - online off line status updated - echo added to rename --- app/src/components/footer/Footer.tsx | 35 +++++++++++++++++-- app/src/components/icons/DashboardIcon.tsx | 17 +++++++++ .../components/ui/features/RenameTooltip.tsx | 2 +- app/src/components/ui/inputs/RenameInput.tsx | 14 +++++--- app/src/pages/Project.tsx | 3 +- .../simulation/products/renameProductApi.ts | 3 +- app/src/styles/components/footer/_footer.scss | 32 ++++++++++++++--- 7 files changed, 92 insertions(+), 14 deletions(-) diff --git a/app/src/components/footer/Footer.tsx b/app/src/components/footer/Footer.tsx index 94ee0b9..39273df 100644 --- a/app/src/components/footer/Footer.tsx +++ b/app/src/components/footer/Footer.tsx @@ -1,5 +1,5 @@ -import React, { useEffect } from "react"; -import { HelpIcon } from "../icons/DashboardIcon"; +import React, { useEffect, useState } from "react"; +import { HelpIcon, WifiIcon } from "../icons/DashboardIcon"; import { useLogger } from "../ui/log/LoggerContext"; import { GetLogIcon } from "./getLogIcons"; import { @@ -31,6 +31,27 @@ const Footer: React.FC = () => { const { Leftnote, Middlenote, Rightnote } = useMouseNoteStore(); + const [isOnline, setIsOnline] = useState(navigator.onLine); + + useEffect(() => { + const handleOnline = () => { + echo.success('You are back Online'); + setIsOnline(true); + }; + const handleOffline = () => { + echo.warn('Changes made now might not be saved'); + echo.error('You are now Offline.'); + setIsOnline(false); + }; + + window.addEventListener("online", handleOnline); + window.addEventListener("offline", handleOffline); + + return () => { + window.removeEventListener("online", handleOnline); + window.removeEventListener("offline", handleOffline); + }; + }, []); const mouseButtons = [ { icon: , @@ -98,6 +119,16 @@ const Footer: React.FC = () => {
+
+
+ +
+
{isOnline ? "Online" : "Offline"}
+
diff --git a/app/src/components/icons/DashboardIcon.tsx b/app/src/components/icons/DashboardIcon.tsx index 1aa94b5..3eb5301 100644 --- a/app/src/components/icons/DashboardIcon.tsx +++ b/app/src/components/icons/DashboardIcon.tsx @@ -253,3 +253,20 @@ export function LogoutIcon() { ); } + +export function WifiIcon() { + return ( + + + + ); +} diff --git a/app/src/components/ui/features/RenameTooltip.tsx b/app/src/components/ui/features/RenameTooltip.tsx index 87de122..d55c5d7 100644 --- a/app/src/components/ui/features/RenameTooltip.tsx +++ b/app/src/components/ui/features/RenameTooltip.tsx @@ -4,7 +4,6 @@ import { useLeftData, useTopData, } from "../../../store/visualization/useZone3DWidgetStore"; -import { useRenameModeStore } from "../../../store/builder/store"; type RenameTooltipProps = { name: string; @@ -20,6 +19,7 @@ const RenameTooltip: React.FC = ({ name, onSubmit }) => { const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); onSubmit(value.trim()); + echo.info(`Selected Object has been renamed to ${value.trim()}`); setTop(0); setLeft(0); }; diff --git a/app/src/components/ui/inputs/RenameInput.tsx b/app/src/components/ui/inputs/RenameInput.tsx index 637dec3..31cc311 100644 --- a/app/src/components/ui/inputs/RenameInput.tsx +++ b/app/src/components/ui/inputs/RenameInput.tsx @@ -12,7 +12,12 @@ interface RenameInputProps { canEdit?: boolean; } -const RenameInput: React.FC = ({ value, onRename, checkDuplicate, canEdit = true }) => { +const RenameInput: React.FC = ({ + value, + onRename, + checkDuplicate, + canEdit = true, +}) => { const [isEditing, setIsEditing] = useState(false); const [text, setText] = useState(value); const [isDuplicate, setIsDuplicate] = useState(false); @@ -36,10 +41,10 @@ const RenameInput: React.FC = ({ value, onRename, checkDuplica }; const handleBlur = () => { - - if (isDuplicate) return + if (isDuplicate) return; setIsEditing(false); if (onRename && !isDuplicate) { + echo.info(`Selected Object has been renamed to ${text}`) onRename(text); } }; @@ -52,6 +57,7 @@ const RenameInput: React.FC = ({ value, onRename, checkDuplica if (e.key === "Enter" && !isDuplicate) { setIsEditing(false); if (onRename) { + echo.info(`Selected Object has been renamed to ${text}`) onRename(text); } } @@ -80,4 +86,4 @@ const RenameInput: React.FC = ({ value, onRename, checkDuplica ); }; -export default RenameInput +export default RenameInput; diff --git a/app/src/pages/Project.tsx b/app/src/pages/Project.tsx index d7bebd8..f3f369a 100644 --- a/app/src/pages/Project.tsx +++ b/app/src/pages/Project.tsx @@ -101,6 +101,7 @@ const Project: React.FC = () => { if (email) { const token = localStorage.getItem("token"); const refreshToken = localStorage.getItem("refreshToken"); + echo.warn('Validating token'); if (token) { useSocketStore .getState() @@ -110,7 +111,7 @@ const Project: React.FC = () => { setOrganization(organization); setUserName(userName); } - echo.success("Log in successful"); + echo.success("Project initialized and loaded successfully"); } else { navigate("/"); } diff --git a/app/src/services/simulation/products/renameProductApi.ts b/app/src/services/simulation/products/renameProductApi.ts index d64f0da..d43997e 100644 --- a/app/src/services/simulation/products/renameProductApi.ts +++ b/app/src/services/simulation/products/renameProductApi.ts @@ -25,6 +25,7 @@ export const renameProductApi = async (body: { if (!response.ok) { console.error("Failed to rename product"); + echo.error("Failed to rename product"); } const result = await response.json(); @@ -32,11 +33,11 @@ export const renameProductApi = async (body: { return result; } catch (error) { echo.error("Failed to rename product Api"); - if (error instanceof Error) { console.log(error.message); } else { console.log("An unknown error occurred"); + echo.log("An unknown error occurred"); } } }; diff --git a/app/src/styles/components/footer/_footer.scss b/app/src/styles/components/footer/_footer.scss index 152a39f..0b490ee 100644 --- a/app/src/styles/components/footer/_footer.scss +++ b/app/src/styles/components/footer/_footer.scss @@ -63,7 +63,7 @@ } .bg-dummy.right-bottom { - right: 68px; + right: 84px; bottom: 0; width: 20%; height: 100%; @@ -79,7 +79,8 @@ } .logs-detail, - .version { + .version, + .wifi-connection { @include flex-center; border-radius: #{$border-radius-extra-large}; padding: 3px 6px; @@ -107,7 +108,28 @@ } } - .version { + .wifi-connection { + .tooltip { + transform: translateX(-16px); + &::after { + left: 76%; + } + } + &:hover { + .tooltip { + opacity: 1; + } + } + &.connected svg path { + fill: #1ec018; + } + &.disconnected svg path { + fill: #e44405; + } + } + + .version, + .wifi-connection { background: var(--background-color); font-size: var(--font-size-tiny); @@ -179,7 +201,7 @@ background: var(--background-color); border-radius: #{$border-radius-medium}; outline: 1px solid var(--border-color); - &:hover{ + &:hover { background: var(--background-color-solid); } } @@ -288,7 +310,7 @@ font-family: monospace; font-size: var(--font-size-tiny); color: var(--icon-default-color-active); - &:last-child{ + &:last-child { background: var(--background-color-button); } } From 9825c3ef1205e9e4d98feb075eb1d4aae034404c Mon Sep 17 00:00:00 2001 From: Vishnu Date: Fri, 29 Aug 2025 17:58:37 +0530 Subject: [PATCH 34/34] worker img update --- app/src/assets/image/categories/worker.png | Bin 40000 -> 40000 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/app/src/assets/image/categories/worker.png b/app/src/assets/image/categories/worker.png index e2287e7ae05c2d1c449ad396f43339e45d273bf1..bdd26c1909b99233ba912348ffe51b2770a3c6d5 100644 GIT binary patch delta 25322 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 literal 40000 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