From 21d96a31bc56542cc1762f81c8069d96f90f273e Mon Sep 17 00:00:00 2001 From: Poovizhi99 Date: Tue, 1 Apr 2025 08:58:56 +0530 Subject: [PATCH 01/12] searching filtered assets --- .../components/layout/sidebarLeft/Assets.tsx | 104 ++++++++++++----- .../analysis/RenderAnalysisInputs.tsx | 1 + .../properties/GlobalProperties.tsx | 105 +++++++++++++++--- app/src/components/ui/inputs/InputRange.tsx | 29 ++++- app/src/modules/market/Card.tsx | 1 + app/src/modules/market/FilterSearch.tsx | 1 - app/src/modules/scene/world/world.tsx | 6 + .../environment/findEnvironment.ts | 63 ++++++----- .../environment/setEnvironment.ts | 63 +++++++---- app/src/services/marketplace/fetchAssets.ts | 4 +- app/src/store/store.ts | 14 ++- app/src/styles/components/input.scss | 2 +- .../components/marketPlace/marketPlace.scss | 4 +- app/src/styles/layout/sidebar.scss | 49 ++++++++ 14 files changed, 342 insertions(+), 104 deletions(-) diff --git a/app/src/components/layout/sidebarLeft/Assets.tsx b/app/src/components/layout/sidebarLeft/Assets.tsx index f8733dc..6005546 100644 --- a/app/src/components/layout/sidebarLeft/Assets.tsx +++ b/app/src/components/layout/sidebarLeft/Assets.tsx @@ -9,12 +9,13 @@ import { getCategoryAsset } from "../../../services/factoryBuilder/assest/assets import arch from "../../../assets/gltf-glb/arch.glb"; import door from "../../../assets/gltf-glb/door.glb"; import window from "../../../assets/gltf-glb/window.glb"; +import { fetchAssets } from "../../../services/marketplace/fetchAssets"; interface AssetProp { filename: string; thumbnail?: string; category: string; description?: string; - tags?: string; + tags: string; url?: String; uploadDate?: number; isArchieve?: boolean; @@ -25,12 +26,27 @@ interface AssetProp { const Assets: React.FC = () => { const [searchValue, setSearchValue] = useState(""); const [selectedCategory, setSelectedCategory] = useState(null); - const [filteredAsset, setFilteredAsset] = useState([]); + const [categoryAssets, setCategoryAssets] = useState([]); + const [filtereredAssets, setFiltereredAssets] = useState([]); const handleSearchChange = (value: string) => { setSearchValue(value); - setSelectedCategory(null); // Reset selected category when search changes + setSelectedCategory(null); + const filteredModels = filtereredAssets?.filter((model) => + model.filename.toLowerCase().includes(value.toLowerCase()) + ); + + setCategoryAssets(filteredModels); }; + useEffect(() => { + const filteredAssets = async () => { + try { + const filt = await fetchAssets(); + setFiltereredAssets(filt); + } catch {} + }; + filteredAssets(); + }, []); const categoryList = useMemo( () => [ @@ -68,56 +84,53 @@ const Assets: React.FC = () => { filename: "arch", category: "Feneration", url: arch, + tags: "arch", }, { filename: "door", category: "Feneration", url: door, + thumbnail: feneration, + tags: "door", }, { filename: "window", category: "Feneration", url: window, + tags: "window", }, ]; - setFilteredAsset(localAssets); + setCategoryAssets(localAssets); + setFiltereredAssets(localAssets); } else { try { const res = await getCategoryAsset(asset); - setFilteredAsset(res || []); // Ensure it's always an array + setCategoryAssets(res || []); // Ensure it's always an array + setFiltereredAssets(res || []); } catch (error) {} } }; - useEffect(() => {}, [filteredAsset]); return (
{searchValue ? ( -
-

Results for "{searchValue}"

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

Results for "{searchValue}"

+
-

{selectedCategory}

- {filteredAsset && - filteredAsset?.map((asset: any, index: number) => ( + {categoryAssets && + categoryAssets?.map((asset: any, index: number) => (
- {asset?.thumbnail && ( - {asset.filename} - )} + {asset.filename} +
{asset.filename .split("_") @@ -131,6 +144,43 @@ const Assets: React.FC = () => { ))}
+ ) : selectedCategory ? ( +
+ {/* Back Button */} +
{ + setSelectedCategory(null); + setCategoryAssets([]); + }} + > + ← Back +
+

{selectedCategory}

+
+ {searchValue || + (categoryAssets && + categoryAssets?.map((asset: any, index: number) => ( +
+ {asset.filename} + +
+ {asset.filename + .split("_") + .map( + (word: any) => + word.charAt(0).toUpperCase() + word.slice(1) + ) + .join(" ")} +
+
+ )))} +
+
) : (

Categories

diff --git a/app/src/components/layout/sidebarRight/analysis/RenderAnalysisInputs.tsx b/app/src/components/layout/sidebarRight/analysis/RenderAnalysisInputs.tsx index 3ab886f..e14b542 100644 --- a/app/src/components/layout/sidebarRight/analysis/RenderAnalysisInputs.tsx +++ b/app/src/components/layout/sidebarRight/analysis/RenderAnalysisInputs.tsx @@ -33,6 +33,7 @@ const RenderAnalysisInputs: React.FC = ({ label={preset.inputs.label} min={0} max={0} + value={5} /> ); } diff --git a/app/src/components/layout/sidebarRight/properties/GlobalProperties.tsx b/app/src/components/layout/sidebarRight/properties/GlobalProperties.tsx index bd7e5c1..d65a10c 100644 --- a/app/src/components/layout/sidebarRight/properties/GlobalProperties.tsx +++ b/app/src/components/layout/sidebarRight/properties/GlobalProperties.tsx @@ -1,4 +1,4 @@ -import React, { useState } from "react"; +import React, { useEffect, useState } from "react"; import InputRange from "../../../ui/inputs/InputRange"; import InputToggle from "../../../ui/inputs/InputToggle"; import { AI_Icon } from "../../../icons/ExportCommonIcons"; @@ -6,6 +6,7 @@ import LabeledButton from "../../../ui/inputs/LabledButton"; import { useAzimuth, useElevation, + useLimitDistance, useRenderDistance, useResetCamera, useRoofVisibility, @@ -17,6 +18,7 @@ import { } from "../../../../store/store"; import { setEnvironment } from "../../../../services/factoryBuilder/environment/setEnvironment"; import * as CONSTANTS from "../../../../types/world/worldConstants"; +import { validateBBox } from "@turf/helpers"; const GlobalProperties: React.FC = () => { const { toggleView, setToggleView } = useToggleView(); const { selectedWallItem, setSelectedWallItem } = useSelectedWallItem(); @@ -27,27 +29,83 @@ const GlobalProperties: React.FC = () => { const { elevation, setElevation } = useElevation(); const { azimuth, setAzimuth } = useAzimuth(); const { renderDistance, setRenderDistance } = useRenderDistance(); + const { socket } = useSocketStore(); - const [limitDistance, setLimitDistance] = useState(false); - const [distance, setDistance] = useState(30); + const { limitDistance, setLimitDistance } = useLimitDistance(); + const [distance, setDistance] = useState(40); + useEffect(() => {}, [limitDistance]); const [limitGridDistance, setLimitGridDistance] = useState(false); const [gridDistance, setGridDistance] = useState(5); - function optimizeScene() { + const optimizeScene = async (value: any) => { + const email = localStorage.getItem("email"); + const organization = email?.split("@")[1]?.split(".")[0] || "defaultOrg"; + + const data = await setEnvironment( + organization, + localStorage.getItem("userId")!, + wallVisibility, + roofVisibility, + shadows, + 30, + true + ); + setRenderDistance(30); setLimitDistance(true); - setDistance(30); - } + }; + const limitRenderDistance = async () => { + const email = localStorage.getItem("email"); + const organization = email?.split("@")[1]?.split(".")[0] || "defaultOrg"; + + if (limitDistance) { + let data = await setEnvironment( + organization, + localStorage.getItem("userId")!, + wallVisibility, + roofVisibility, + shadows, + 75, + !limitDistance + ); + setRenderDistance(75); + } else { + let data = await setEnvironment( + organization, + localStorage.getItem("userId")!, + wallVisibility, + roofVisibility, + shadows, + renderDistance, + !limitDistance + ); + } + setLimitDistance(!limitDistance); + }; function updateDistance(value: number) { - console.log("value: ", value); setDistance(value); setRenderDistance(value); } - function updateGridDistance(value: number) { setGridDistance(value); } + const updatedDist = async (value: number) => { + const email = localStorage.getItem("email"); + const organization = email?.split("@")[1]?.split(".")[0] || "defaultOrg"; + setRenderDistance(value); + // setDistance(value); + const data = await setEnvironment( + organization, + localStorage.getItem("userId")!, + wallVisibility, + roofVisibility, + shadows, + value, + limitDistance + ); + }; + // Function to toggle roof visibility const changeRoofVisibility = async () => { const email = localStorage.getItem("email"); @@ -59,9 +117,11 @@ const GlobalProperties: React.FC = () => { localStorage.getItem("userId")!, wallVisibility, !roofVisibility, - shadows + shadows, + renderDistance, + limitDistance ); - // console.log('data: ', data); + // //using Socket // const visData = { @@ -86,9 +146,11 @@ const GlobalProperties: React.FC = () => { localStorage.getItem("userId")!, !wallVisibility, roofVisibility, - shadows + shadows, + renderDistance, + limitDistance ); - // console.log('data: ', data); + // //using Socket // const visData = { @@ -113,9 +175,11 @@ const GlobalProperties: React.FC = () => { localStorage.getItem("userId")!, wallVisibility, roofVisibility, - !shadows + !shadows, + renderDistance, + limitDistance ); - // console.log('data: ', data); + // //using Socket // const visData = { @@ -185,18 +249,23 @@ const GlobalProperties: React.FC = () => { inputKey="4" label="Limit Render Distance" value={limitDistance} - onClick={() => { - setLimitDistance(!limitDistance); + // onClick={() => { + // setLimitDistance(!limitDistance); + // // setDistance(75); + // // setRenderDistance(75); + // }} + onClick={async () => { + await limitRenderDistance(); // Call the function here }} /> updateDistance(value)} + onPointerUp={updatedDist} />
diff --git a/app/src/components/ui/inputs/InputRange.tsx b/app/src/components/ui/inputs/InputRange.tsx index a09a782..51c8b1a 100644 --- a/app/src/components/ui/inputs/InputRange.tsx +++ b/app/src/components/ui/inputs/InputRange.tsx @@ -8,6 +8,7 @@ interface InputToggleProps { onChange?: (value: number) => void; // Function to handle toggle clicks disabled?: boolean; value?: number; + onPointerUp?: (value: number) => void; } const InputRange: React.FC = ({ @@ -17,9 +18,10 @@ const InputRange: React.FC = ({ min, max, disabled, - value = 5, + value, + onPointerUp, }) => { - const [rangeValue, setRangeValue] = useState(value); + const [rangeValue, setRangeValue] = useState(value ? value : 5); function handleChange(e: React.ChangeEvent) { const newValue = parseInt(e.target.value); // Parse the value to an integer @@ -31,8 +33,22 @@ const InputRange: React.FC = ({ } } useEffect(() => { - setRangeValue(value); + value && setRangeValue(value); }, [value]); + function handlePointerUp(e: React.PointerEvent) { + const newValue = parseInt(e.currentTarget.value, 10); // Parse value correctly + + if (onPointerUp) { + onPointerUp(newValue); // Call the callback function if it exists + } + } + function handlekey(e: React.KeyboardEvent) { + const newValue = parseInt(e.currentTarget.value, 10); // Parse value correctly + + if (onPointerUp) { + onPointerUp(newValue); // Call the callback function if it exists + } + } return (
@@ -52,6 +68,7 @@ const InputRange: React.FC = ({ onChange={handleChange} disabled={disabled} value={rangeValue} + onPointerUp={handlePointerUp} /> = ({ value={rangeValue} onChange={handleChange} disabled={disabled} + onKeyUp={(e) => { + if (e.key === "ArrowUp" || e.key === "ArrowDown") { + console.log("e.key: ", e.key); + handlekey(e); + } + }} />
diff --git a/app/src/modules/market/Card.tsx b/app/src/modules/market/Card.tsx index ec9db06..c1a3289 100644 --- a/app/src/modules/market/Card.tsx +++ b/app/src/modules/market/Card.tsx @@ -38,6 +38,7 @@ const Card: React.FC = ({ description, onSelectCard, }) => { + console.log('description: ', description); const handleCardSelect = () => { onSelectCard({ assetName, uploadedOn, price, rating, views, description }); }; diff --git a/app/src/modules/market/FilterSearch.tsx b/app/src/modules/market/FilterSearch.tsx index 630942d..de518c8 100644 --- a/app/src/modules/market/FilterSearch.tsx +++ b/app/src/modules/market/FilterSearch.tsx @@ -57,7 +57,6 @@ const FilterSearch: React.FC = ({ const filteredModel = filteredModels?.filter((model) => model.filename.toLowerCase().includes(val.toLowerCase()) ); - setModels(filteredModel); }; diff --git a/app/src/modules/scene/world/world.tsx b/app/src/modules/scene/world/world.tsx index eb7d555..5864ec8 100644 --- a/app/src/modules/scene/world/world.tsx +++ b/app/src/modules/scene/world/world.tsx @@ -30,6 +30,8 @@ import { useWalls, useToolMode, useRefTextUpdate, + useRenderDistance, + useLimitDistance, } from "../../../store/store"; ////////// 3D Function Imports ////////// @@ -117,6 +119,8 @@ export default function World() { const { roofVisibility, setRoofVisibility } = useRoofVisibility(); const { wallVisibility, setWallVisibility } = useWallVisibility(); const { shadows, setShadows } = useShadows(); + const { renderDistance, setRenderDistance } = useRenderDistance(); + const { limitDistance, setLimitDistance } = useLimitDistance(); const { updateScene, setUpdateScene } = useUpdateScene(); const { walls, setWalls } = useWalls(); const { refTextupdate, setRefTextUpdate } = useRefTextUpdate(); @@ -200,6 +204,8 @@ export default function World() { setRoofVisibility(visibility.roofVisibility); setWallVisibility(visibility.wallVisibility); setShadows(visibility.shadowVisibility); + setRenderDistance(visibility.renderDistance); + setLimitDistance(visibility.limitDistance); } } fetchVisibility(); diff --git a/app/src/services/factoryBuilder/environment/findEnvironment.ts b/app/src/services/factoryBuilder/environment/findEnvironment.ts index 525bdda..de5b6a6 100644 --- a/app/src/services/factoryBuilder/environment/findEnvironment.ts +++ b/app/src/services/factoryBuilder/environment/findEnvironment.ts @@ -1,32 +1,43 @@ -import { setEnvironment } from './setEnvironment'; +import { setEnvironment } from "./setEnvironment"; let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`; export const findEnvironment = async (organization: string, userId: string) => { - try { - const response = await fetch(`${url_Backend_dwinzo}/api/v1/findEnvironments/${organization}/${userId}`, { - method: "GET", - headers: { - "Content-Type": "application/json", - }, - }); + try { + const response = await fetch( + `${url_Backend_dwinzo}/api/v1/findEnvironments/${organization}/${userId}`, + { + method: "GET", + headers: { + "Content-Type": "application/json", + }, + } + ); - if (!response.ok) { - throw new Error("Failed to get wall and roof visibility"); - } - - const result = await response.json(); - if (result === "user not found") { - const userpos = setEnvironment(organization, userId, false, false, false); - return userpos; - } else { - return result; - } - } catch (error) { - if (error instanceof Error) { - throw new Error(error.message); - } else { - throw new Error("An unknown error occurred"); - } + if (!response.ok) { + throw new Error("Failed to get wall and roof visibility"); } -}; \ No newline at end of file + + const result = await response.json(); + if (result === "user not found") { + const userpos = setEnvironment( + organization, + userId, + false, + false, + false, + 0, + true + ); + return userpos; + } else { + return result; + } + } catch (error) { + if (error instanceof Error) { + throw new Error(error.message); + } else { + throw new Error("An unknown error occurred"); + } + } +}; diff --git a/app/src/services/factoryBuilder/environment/setEnvironment.ts b/app/src/services/factoryBuilder/environment/setEnvironment.ts index 070146f..1f72959 100644 --- a/app/src/services/factoryBuilder/environment/setEnvironment.ts +++ b/app/src/services/factoryBuilder/environment/setEnvironment.ts @@ -1,26 +1,45 @@ let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`; -export const setEnvironment = async (organization: string, userId: string, wallVisibility: Boolean, roofVisibility: Boolean, shadowVisibility: Boolean) => { - try { - const response = await fetch(`${url_Backend_dwinzo}/api/v1/setEvironments`, { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ organization, userId, wallVisibility, roofVisibility, shadowVisibility }), - }); +export const setEnvironment = async ( + organization: string, + userId: string, + wallVisibility: Boolean, + roofVisibility: Boolean, + shadowVisibility: Boolean, + renderDistance: number, + limitDistance: boolean +) => { + try { + const response = await fetch( + `${url_Backend_dwinzo}/api/v1/setEvironments`, + { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + organization, + userId, + wallVisibility, + roofVisibility, + shadowVisibility, + renderDistance, + limitDistance, + }), + } + ); - if (!response.ok) { - throw new Error("Failed to set wall and roof visibility"); - } - - const result = await response.json(); - return result; - } catch (error) { - if (error instanceof Error) { - throw new Error(error.message); - } else { - throw new Error("An unknown error occurred"); - } + if (!response.ok) { + throw new Error("Failed to set wall and roof visibility"); } -}; \ No newline at end of file + + const result = await response.json(); + return result; + } catch (error) { + if (error instanceof Error) { + throw new Error(error.message); + } else { + throw new Error("An unknown error occurred"); + } + } +}; diff --git a/app/src/services/marketplace/fetchAssets.ts b/app/src/services/marketplace/fetchAssets.ts index e434a2b..7a395f9 100644 --- a/app/src/services/marketplace/fetchAssets.ts +++ b/app/src/services/marketplace/fetchAssets.ts @@ -6,8 +6,8 @@ export const fetchAssets = async () => { throw new Error("Network response was not ok"); } const result = await response.json(); - const last10Assets = result.slice(-10); - console.log('last10Assets: ', last10Assets); + // const last10Assets = result.slice(-10); + // console.log('last10Assets: ', last10Assets); return result; } catch (error) { console.log("error: ", error); diff --git a/app/src/store/store.ts b/app/src/store/store.ts index a64f417..fe35801 100644 --- a/app/src/store/store.ts +++ b/app/src/store/store.ts @@ -208,7 +208,9 @@ export const useActiveLayer = create((set: any) => ({ interface RefTextUpdateState { refTextupdate: number; - setRefTextUpdate: (callback: (currentValue: number) => number | number) => void; + setRefTextUpdate: ( + callback: (currentValue: number) => number | number + ) => void; } export const useRefTextUpdate = create((set) => ({ @@ -216,7 +218,9 @@ export const useRefTextUpdate = create((set) => ({ setRefTextUpdate: (callback) => set((state) => ({ refTextupdate: - typeof callback === "function" ? callback(state.refTextupdate) : callback, + typeof callback === "function" + ? callback(state.refTextupdate) + : callback, })), })); @@ -251,7 +255,7 @@ export const useAzimuth = create((set: any) => ({ })); export const useRenderDistance = create((set: any) => ({ - renderDistance: 50, + renderDistance: 40, setRenderDistance: (x: any) => set({ renderDistance: x }), })); @@ -375,3 +379,7 @@ export const useWidgetSubOption = create((set: any) => ({ widgetSubOption: "2D", setWidgetSubOption: (x: any) => set({ widgetSubOption: x }), })); +export const useLimitDistance = create((set: any) => ({ + limitDistance: true, + setLimitDistance: (x: any) => set({ limitDistance: x }), +})); diff --git a/app/src/styles/components/input.scss b/app/src/styles/components/input.scss index f60aca6..64eaaea 100644 --- a/app/src/styles/components/input.scss +++ b/app/src/styles/components/input.scss @@ -558,7 +558,7 @@ input { } .input-value { - width: 40px; + width: 42px; text-align: center; } } diff --git a/app/src/styles/components/marketPlace/marketPlace.scss b/app/src/styles/components/marketPlace/marketPlace.scss index 620d934..b3de954 100644 --- a/app/src/styles/components/marketPlace/marketPlace.scss +++ b/app/src/styles/components/marketPlace/marketPlace.scss @@ -114,7 +114,9 @@ flex-direction: column; justify-content: center; gap: 6px; - + .assets-container { + height: auto; + } .icon { position: absolute; top: 12px; diff --git a/app/src/styles/layout/sidebar.scss b/app/src/styles/layout/sidebar.scss index e8175c5..f028f76 100644 --- a/app/src/styles/layout/sidebar.scss +++ b/app/src/styles/layout/sidebar.scss @@ -936,6 +936,7 @@ padding: 0 6px; .assets-wrapper { + width: 100%; position: relative; margin: 8px 10px; @@ -1051,3 +1052,51 @@ } } } + +.assets-container { + width: 100%; + display: flex; + flex-direction: row; + flex-wrap: wrap; + height: 100%; + gap: 3px; + padding: 10px 0; + + .assets { + width: 117px; + height: 95px; + border-radius: 3.59px; + background-color: var(--background-color-gray); + padding: 8px; + padding-top: 12px; + font-weight: $medium-weight; + position: relative; + overflow: hidden; + + .asset-name { + position: relative; + z-index: 3; + font-size: var(--font-size-regular); + } + + .asset-image { + height: 100%; + width: 100%; + position: absolute; + // top: 50%; + // right: 5px; + // transform: translate(0, -50%); + top: 0; + left: 0; + z-index: 2; + } + } +} +.assets-result { + width: 100%; + height: 100%; + margin: 8px 10px; + .assets-wrapper { + margin: 0; + } +} From 526befad2080298def3296787a2aefb8007424d6 Mon Sep 17 00:00:00 2001 From: Jerald-Golden-B Date: Tue, 1 Apr 2025 14:25:42 +0530 Subject: [PATCH 02/12] refactor: update backend URL and added conveyor event storing in ackend --- app/.env | 3 - .../ui/componets/RealTimeVisulization.tsx | 2 +- .../geomentries/assets/addAssetModel.ts | 8 +- .../geomentries/assets/deleteFloorItems.ts | 6 +- .../builder/groups/floorItemsGroup.tsx | 5 +- app/src/modules/collaboration/collabCams.tsx | 19 +++- .../collaboration/socketResponses.dev.tsx | 76 +++++++++++++--- .../scene/IntialLoad/loadInitialFloorItems.ts | 86 +++++++++++++------ .../controls/selection/copyPasteControls.tsx | 2 +- .../selection/duplicationControls.tsx | 2 +- .../scene/controls/selection/moveControls.tsx | 2 +- .../controls/selection/rotateControls.tsx | 2 +- .../controls/selection/selectionControls.tsx | 2 +- .../scene/controls/transformControls.tsx | 2 +- .../simulation/behaviour/behaviour.tsx | 6 +- .../modules/simulation/path/pathCreation.tsx | 6 +- .../assest/floorAsset/getFloorItemsApi.ts | 6 +- .../assest/floorAsset/setFloorItemApi.ts | 2 +- .../services/simulation/getAssetEventType.ts | 2 +- app/src/types/world/worldTypes.d.ts | 6 +- 20 files changed, 173 insertions(+), 72 deletions(-) diff --git a/app/.env b/app/.env index daf92f1..e0b0d16 100644 --- a/app/.env +++ b/app/.env @@ -7,9 +7,6 @@ REACT_APP_SERVER_SOCKET_API_BASE_URL=185.100.212.76:8000 # Base URL for the server REST API, used for HTTP requests to the backend server. REACT_APP_SERVER_REST_API_BASE_URL=185.100.212.76:5000 -REACT_APP_SERVER_REST_API_LOCAL_BASE_URL=192.168.0.102:5000 - -# Base URL for the server marketplace API. # REACT_APP_SERVER_MARKETPLACE_URL=185.100.212.76:50011 REACT_APP_SERVER_MARKETPLACE_URL=192.168.0.111:3501 diff --git a/app/src/components/ui/componets/RealTimeVisulization.tsx b/app/src/components/ui/componets/RealTimeVisulization.tsx index f6a041a..4ff643b 100644 --- a/app/src/components/ui/componets/RealTimeVisulization.tsx +++ b/app/src/components/ui/componets/RealTimeVisulization.tsx @@ -63,7 +63,7 @@ const RealTimeVisulization: React.FC = () => { const organization = email?.split("@")[1]?.split(".")[0]; try { const response = await getZone2dData(organization); - console.log('response: ', response); + // console.log('response: ', response); if (!Array.isArray(response)) { return; diff --git a/app/src/modules/builder/geomentries/assets/addAssetModel.ts b/app/src/modules/builder/geomentries/assets/addAssetModel.ts index 65832f8..0cf9f22 100644 --- a/app/src/modules/builder/geomentries/assets/addAssetModel.ts +++ b/app/src/modules/builder/geomentries/assets/addAssetModel.ts @@ -60,7 +60,7 @@ async function addAssetModel( if (intersectPoint.y < 0) { intersectPoint = new THREE.Vector3(intersectPoint.x, 0, intersectPoint.z); } - console.log('selectedItem: ', selectedItem); + // console.log('selectedItem: ', selectedItem); const cachedModel = THREE.Cache.get(selectedItem.id); if (cachedModel) { // console.log(`[Cache] Fetching ${selectedItem.name}`); @@ -145,7 +145,7 @@ async function handleModelLoad( }; const email = localStorage.getItem("email"); - const organization = email ? email.split("@")[1].split(".")[0] : "default"; + const organization = email ? email.split("@")[1].split(".")[0] : ""; getAssetEventType(selectedItem.id, organization).then(async (res) => { console.log('res: ', res); @@ -177,7 +177,7 @@ async function handleModelLoad( speed: 'Inherit' }; - console.log('eventData: ', eventData); + // console.log('eventData: ', eventData); newFloorItem.eventData = eventData; } @@ -215,6 +215,8 @@ async function handleModelLoad( eventData: newFloorItem.eventData, socketId: socket.id }; + console.log('data: ', data); + socket.emit("v2:model-asset:add", data); diff --git a/app/src/modules/builder/geomentries/assets/deleteFloorItems.ts b/app/src/modules/builder/geomentries/assets/deleteFloorItems.ts index 87930dc..3bbe2cc 100644 --- a/app/src/modules/builder/geomentries/assets/deleteFloorItems.ts +++ b/app/src/modules/builder/geomentries/assets/deleteFloorItems.ts @@ -2,9 +2,9 @@ import { toast } from 'react-toastify'; import * as THREE from 'three'; import * as Types from "../../../../types/world/worldTypes"; -import { getFloorItems } from '../../../../services/factoryBuilder/assest/floorAsset/getFloorItemsApi'; // import { deleteFloorItem } from '../../../../services/factoryBuilder/assest/floorAsset/deleteFloorItemApi'; import { Socket } from 'socket.io-client'; +import { getFloorAssets } from '../../../../services/factoryBuilder/assest/floorAsset/getFloorItemsApi'; async function DeleteFloorItems( itemsGroup: Types.RefGroup, @@ -20,7 +20,7 @@ async function DeleteFloorItems( const email = localStorage.getItem('email') const organization = (email!.split("@")[1]).split(".")[0]; - const items = await getFloorItems(organization); + const items = await getFloorAssets(organization); const removedItem = items.find( (item: { modeluuid: string }) => item.modeluuid === hoveredDeletableFloorItem.current?.uuid ); @@ -42,7 +42,7 @@ async function DeleteFloorItems( socketId: socket.id } - const response = socket.emit('v1:FloorItems:delete', data) + const response = socket.emit('v2:model-asset:delete', data) if (response) { const updatedItems = items.filter( diff --git a/app/src/modules/builder/groups/floorItemsGroup.tsx b/app/src/modules/builder/groups/floorItemsGroup.tsx index 3b38d01..7e7282b 100644 --- a/app/src/modules/builder/groups/floorItemsGroup.tsx +++ b/app/src/modules/builder/groups/floorItemsGroup.tsx @@ -11,7 +11,7 @@ import DeletableHoveredFloorItems from "../geomentries/assets/deletableHoveredFl import DeleteFloorItems from "../geomentries/assets/deleteFloorItems"; import loadInitialFloorItems from "../../scene/IntialLoad/loadInitialFloorItems"; import addAssetModel from "../geomentries/assets/addAssetModel"; -import { getFloorItems } from "../../../services/factoryBuilder/assest/floorAsset/getFloorItemsApi"; +import { getFloorAssets } from "../../../services/factoryBuilder/assest/floorAsset/getFloorItemsApi"; import useModuleStore from "../../../store/useModuleStore"; // import { retrieveGLTF } from "../../../utils/indexDB/idbUtils"; const assetManagerWorker = new Worker(new URL('../../../services/factoryBuilder/webWorkers/assetManagerWorker.js', import.meta.url)); @@ -59,8 +59,7 @@ const FloorItemsGroup = ({ itemsGroup, hoveredDeletableFloorItem, AttachedObject } }; - getFloorItems(organization).then((data) => { - console.log('data: ', data); + getFloorAssets(organization).then((data) => { const uniqueItems = (data as Types.FloorItems).filter((item, index, self) => index === self.findIndex((t) => t.modelfileID === item.modelfileID) ); diff --git a/app/src/modules/collaboration/collabCams.tsx b/app/src/modules/collaboration/collabCams.tsx index 5ebe1eb..b15d17d 100644 --- a/app/src/modules/collaboration/collabCams.tsx +++ b/app/src/modules/collaboration/collabCams.tsx @@ -32,7 +32,7 @@ const CamModelsGroup = () => { if (!socket) return; const organization = email!.split("@")[1].split(".")[0]; - socket.on("userConnectRespones", (data: any) => { + socket.on("userConnectResponse", (data: any) => { if (!groupRef.current) return; if (data.data.userData.email === email) return; if (socket.id === data.socketId || organization !== data.organization) @@ -64,7 +64,11 @@ const CamModelsGroup = () => { }); }); - socket.on("userDisConnectRespones", (data: any) => { + // socket.on("users:online", (data: any) => { + // console.log('users online: ', data); + // }) + + socket.on("userDisConnectResponse", (data: any) => { if (!groupRef.current) return; if (socket.id === data.socketId || organization !== data.organization) return; @@ -109,6 +113,15 @@ const CamModelsGroup = () => { }; }, [socket, activeUsers]); + + // useEffect(() => { + // console.log(activeUsers); + // }, [activeUsers]) + + // useEffect(() => { + // console.log(models); + // }, [models]) + useFrame(() => { if (!groupRef.current) return; Object.keys(models).forEach((uuid) => { @@ -142,7 +155,7 @@ const CamModelsGroup = () => { const filteredData = data.cameraDatas.filter( (camera: any) => camera.userData.email !== email ); - let a:any = []; + let a: any = []; if (filteredData.length > 0) { loader.load(camModel, (gltf) => { const newCams = filteredData.map((cam: any) => { diff --git a/app/src/modules/collaboration/socketResponses.dev.tsx b/app/src/modules/collaboration/socketResponses.dev.tsx index 1a5ef83..d17557d 100644 --- a/app/src/modules/collaboration/socketResponses.dev.tsx +++ b/app/src/modules/collaboration/socketResponses.dev.tsx @@ -82,14 +82,14 @@ export default function SocketResponses({ }) socket.on('model-asset:response:updates', async (data: any) => { - console.log('data: ', data); + // console.log('data: ', data); if (socket.id === data.socketId) { return } if (organization !== data.organization) { return } - if (data.message === "flooritem created") { + if (data.message === "Model created successfully") { const loader = new GLTFLoader(); const dracoLoader = new DRACOLoader(); @@ -101,10 +101,61 @@ export default function SocketResponses({ isTempLoader.current = true; const cachedModel = THREE.Cache.get(data.data.modelname); let url; - if (cachedModel) { // console.log(`Getting ${data.data.modelname} from cache`); - url = URL.createObjectURL(cachedModel); + const model = cachedModel.scene.clone(); + model.uuid = data.data.modeluuid; + model.userData = { name: data.data.modelname, modelId: data.data.modelFileID }; + model.position.set(...data.data.position as [number, number, number]); + model.rotation.set(data.data.rotation.x, data.data.rotation.y, data.data.rotation.z); + model.scale.set(...CONSTANTS.assetConfig.defaultScaleBeforeGsap); + + model.traverse((child: any) => { + if (child.isMesh) { + // Clone the material to ensure changes are independent + // child.material = child.material.clone(); + + child.castShadow = true; + child.receiveShadow = true; + } + }); + + itemsGroup.current.add(model); + + if (tempLoader.current) { + tempLoader.current.material.dispose(); + tempLoader.current.geometry.dispose(); + itemsGroup.current.remove(tempLoader.current); + tempLoader.current = undefined; + } + + const newFloorItem: Types.FloorItemType = { + modeluuid: data.data.modeluuid, + modelname: data.data.modelname, + modelfileID: data.data.modelfileID, + position: [...data.data.position as [number, number, number]], + rotation: { + x: model.rotation.x, + y: model.rotation.y, + z: model.rotation.z, + }, + isLocked: data.data.isLocked, + isVisible: data.data.isVisible, + }; + + if (data.data.eventData) { + newFloorItem.eventData = data.data.eventData; + } + + setFloorItems((prevItems: any) => { + const updatedItems = [...(prevItems || []), newFloorItem]; + localStorage.setItem("FloorItems", JSON.stringify(updatedItems)); + return updatedItems; + }); + + gsap.to(model.position, { y: data.data.position[1], duration: 1.5, ease: 'power2.out' }); + gsap.to(model.scale, { x: 1, y: 1, z: 1, duration: 1.5, ease: 'power2.out', onComplete: () => { toast.success("Model Added!") } }); + } else { const indexedDBModel = await retrieveGLTF(data.data.modelname); if (indexedDBModel) { @@ -118,7 +169,9 @@ export default function SocketResponses({ } } - loadModel(url); + if (url) { + loadModel(url); + } } catch (error) { console.error('Error fetching asset model:', error); @@ -168,6 +221,10 @@ export default function SocketResponses({ isVisible: data.data.isVisible, }; + if (data.data.eventData) { + newFloorItem.eventData = data.data.eventData; + } + setFloorItems((prevItems: any) => { const updatedItems = [...(prevItems || []), newFloorItem]; localStorage.setItem("FloorItems", JSON.stringify(updatedItems)); @@ -183,7 +240,7 @@ export default function SocketResponses({ }); } - } else if (data.message === "flooritems updated") { + } else if (data.message === "Model updated successfully") { itemsGroup.current.children.forEach((item: THREE.Group) => { if (item.uuid === data.data.modeluuid) { item.position.set(...data.data.position as [number, number, number]); @@ -212,14 +269,14 @@ export default function SocketResponses({ } }) - socket.on('FloorItemsDeleteResponse', (data: any) => { + socket.on('model-asset:response:updates', (data: any) => { if (socket.id === data.socketId) { return } if (organization !== data.organization) { return } - if (data.message === "flooritem deleted") { + if (data.message === "Model deleted successfully") { const deletedUUID = data.data.modeluuid; let items = JSON.parse(localStorage.getItem("FloorItems")!); @@ -746,7 +803,6 @@ export default function SocketResponses({ return } if (data.message === "zone deleted") { - console.log('data: ', data); const updatedZones = zones.filter((zone: any) => zone.zoneId !== data.data.zoneId); setZones(updatedZones); @@ -769,7 +825,7 @@ export default function SocketResponses({ return () => { socket.off('zone:response:updates'); - socket.off('zone:response:updates'); + socket.off('zone:response:delete'); }; }, [socket, zones, zonePoints]) diff --git a/app/src/modules/scene/IntialLoad/loadInitialFloorItems.ts b/app/src/modules/scene/IntialLoad/loadInitialFloorItems.ts index 439cbc3..2b1b1f3 100644 --- a/app/src/modules/scene/IntialLoad/loadInitialFloorItems.ts +++ b/app/src/modules/scene/IntialLoad/loadInitialFloorItems.ts @@ -5,9 +5,9 @@ import * as THREE from 'three'; import * as CONSTANTS from '../../../types/world/worldConstants'; import { toast } from 'react-toastify'; import * as Types from "../../../types/world/worldTypes"; -import { getFloorItems } from '../../../services/factoryBuilder/assest/floorAsset/getFloorItemsApi'; import { initializeDB, retrieveGLTF, storeGLTF } from '../../../utils/indexDB/idbUtils'; import { getCamera } from '../../../services/factoryBuilder/camera/getCameraApi'; +import { getFloorAssets } from '../../../services/factoryBuilder/assest/floorAsset/getFloorItemsApi'; async function loadInitialFloorItems( itemsGroup: Types.RefGroup, @@ -18,7 +18,8 @@ async function loadInitialFloorItems( const email = localStorage.getItem('email'); const organization = (email!.split("@")[1]).split(".")[0]; - const items = await getFloorItems(organization); + const items = await getFloorAssets(organization); + console.log('items: ', items); localStorage.setItem("FloorItems", JSON.stringify(items)); await initializeDB(); @@ -121,18 +122,34 @@ async function loadInitialFloorItems( }); } else { // console.log(`Item ${item.modelname} is not near`); - setFloorItems((prevItems) => [ - ...(prevItems || []), - { - modeluuid: item.modeluuid, - modelname: item.modelname, - position: item.position, - rotation: item.rotation, - modelfileID: item.modelfileID, - isLocked: item.isLocked, - isVisible: item.isVisible, - }, - ]); + if (item.eventData) { + setFloorItems((prevItems) => [ + ...(prevItems || []), + { + modeluuid: item.modeluuid, + modelname: item.modelname, + position: item.position, + rotation: item.rotation, + modelfileID: item.modelfileID, + isLocked: item.isLocked, + isVisible: item.isVisible, + eventData: item.eventData, + }, + ]); + } else { + setFloorItems((prevItems) => [ + ...(prevItems || []), + { + modeluuid: item.modeluuid, + modelname: item.modelname, + position: item.position, + rotation: item.rotation, + modelfileID: item.modelfileID, + isLocked: item.isLocked, + isVisible: item.isVisible, + }, + ]); + } modelsLoaded++; checkLoadingCompletion(modelsLoaded, modelsToLoad, dracoLoader, () => { }); } @@ -169,18 +186,35 @@ function processLoadedModel( itemsGroup?.current?.add(model); - setFloorItems((prevItems) => [ - ...(prevItems || []), - { - modeluuid: item.modeluuid, - modelname: item.modelname, - position: item.position, - rotation: item.rotation, - modelfileID: item.modelfileID, - isLocked: item.isLocked, - isVisible: item.isVisible, - }, - ]); + + if (item.eventData) { + setFloorItems((prevItems) => [ + ...(prevItems || []), + { + modeluuid: item.modeluuid, + modelname: item.modelname, + position: item.position, + rotation: item.rotation, + modelfileID: item.modelfileID, + isLocked: item.isLocked, + isVisible: item.isVisible, + eventData: item.eventData, + }, + ]); + } else { + setFloorItems((prevItems) => [ + ...(prevItems || []), + { + modeluuid: item.modeluuid, + modelname: item.modelname, + position: item.position, + rotation: item.rotation, + modelfileID: item.modelfileID, + isLocked: item.isLocked, + isVisible: item.isVisible, + }, + ]); + } gsap.to(model.position, { y: item.position[1], duration: 1.5, ease: 'power2.out' }); gsap.to(model.scale, { x: 1, y: 1, z: 1, duration: 1.5, ease: 'power2.out' }); diff --git a/app/src/modules/scene/controls/selection/copyPasteControls.tsx b/app/src/modules/scene/controls/selection/copyPasteControls.tsx index 3082462..4628311 100644 --- a/app/src/modules/scene/controls/selection/copyPasteControls.tsx +++ b/app/src/modules/scene/controls/selection/copyPasteControls.tsx @@ -180,7 +180,7 @@ const CopyPasteControls = ({ itemsGroupRef, copiedObjects, setCopiedObjects, pas socketId: socket.id, }; - socket.emit("v1:FloorItems:set", data); + socket.emit("v2:model-asset:add", data); itemsGroupRef.current.add(obj); } diff --git a/app/src/modules/scene/controls/selection/duplicationControls.tsx b/app/src/modules/scene/controls/selection/duplicationControls.tsx index cbe561a..6b32195 100644 --- a/app/src/modules/scene/controls/selection/duplicationControls.tsx +++ b/app/src/modules/scene/controls/selection/duplicationControls.tsx @@ -161,7 +161,7 @@ const DuplicationControls = ({ itemsGroupRef, duplicatedObjects, setDuplicatedOb socketId: socket.id, }; - socket.emit("v1:FloorItems:set", data); + socket.emit("v2:model-asset:add", data); itemsGroupRef.current.add(obj); } diff --git a/app/src/modules/scene/controls/selection/moveControls.tsx b/app/src/modules/scene/controls/selection/moveControls.tsx index bbdb3e1..15973d8 100644 --- a/app/src/modules/scene/controls/selection/moveControls.tsx +++ b/app/src/modules/scene/controls/selection/moveControls.tsx @@ -209,7 +209,7 @@ function MoveControls({ movedObjects, setMovedObjects, itemsGroupRef, copiedObje socketId: socket.id, }; - socket.emit("v1:FloorItems:set", data); + socket.emit("v2:model-asset:add", data); itemsGroupRef.current.add(obj); } diff --git a/app/src/modules/scene/controls/selection/rotateControls.tsx b/app/src/modules/scene/controls/selection/rotateControls.tsx index 6e65839..f8771be 100644 --- a/app/src/modules/scene/controls/selection/rotateControls.tsx +++ b/app/src/modules/scene/controls/selection/rotateControls.tsx @@ -212,7 +212,7 @@ function RotateControls({ rotatedObjects, setRotatedObjects, movedObjects, setMo socketId: socket.id, }; - socket.emit("v1:FloorItems:set", data); + socket.emit("v2:model-asset:add", data); itemsGroupRef.current.add(obj); } diff --git a/app/src/modules/scene/controls/selection/selectionControls.tsx b/app/src/modules/scene/controls/selection/selectionControls.tsx index 9d5f7ea..8e9ac96 100644 --- a/app/src/modules/scene/controls/selection/selectionControls.tsx +++ b/app/src/modules/scene/controls/selection/selectionControls.tsx @@ -222,7 +222,7 @@ const SelectionControls: React.FC = () => { socketId: socket.id }; - socket.emit('v1:FloorItems:delete', data); + socket.emit('v2:model-asset:delete', data); selectedMesh.traverse((child: THREE.Object3D) => { if (child instanceof THREE.Mesh) { diff --git a/app/src/modules/scene/controls/transformControls.tsx b/app/src/modules/scene/controls/transformControls.tsx index 7047f35..0f0aaf0 100644 --- a/app/src/modules/scene/controls/transformControls.tsx +++ b/app/src/modules/scene/controls/transformControls.tsx @@ -83,7 +83,7 @@ export default function TransformControl() { socketId: socket.id } - socket.emit('v1:FloorItems:set', data); + socket.emit('v2:model-asset:add', data); } localStorage.setItem("FloorItems", JSON.stringify(updatedItems)); return updatedItems; diff --git a/app/src/modules/simulation/behaviour/behaviour.tsx b/app/src/modules/simulation/behaviour/behaviour.tsx index 5075384..3cb1df1 100644 --- a/app/src/modules/simulation/behaviour/behaviour.tsx +++ b/app/src/modules/simulation/behaviour/behaviour.tsx @@ -50,8 +50,8 @@ function Behaviour() { connections: { source: { pathUUID: item.modeluuid, pointUUID: point2UUID }, targets: [] }, }, ], - assetPosition: [...item.position], - assetRotation: [item.rotation.x, item.rotation.y, item.rotation.z], + position: [...item.position], + rotation: [item.rotation.x, item.rotation.y, item.rotation.z], speed: 'Inherit', }; @@ -71,7 +71,7 @@ function Behaviour() { connections: { source: { pathUUID: item.modeluuid, pointUUID: pointUUID }, targets: [] }, speed: 2, }, - assetPosition: [...item.position], + position: [...item.position], }; newPaths.push(newVehiclePath); diff --git a/app/src/modules/simulation/path/pathCreation.tsx b/app/src/modules/simulation/path/pathCreation.tsx index 202c7e7..84c3b78 100644 --- a/app/src/modules/simulation/path/pathCreation.tsx +++ b/app/src/modules/simulation/path/pathCreation.tsx @@ -171,8 +171,8 @@ function PathCreation({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObject name={`${path.modeluuid}-event-path`} key={path.modeluuid} ref={el => (groupRefs.current[path.modeluuid] = el!)} - position={path.assetPosition} - rotation={path.assetRotation} + position={path.position} + rotation={path.rotation} onClick={(e) => { if (isConnecting || eyeDropMode) return; e.stopPropagation(); @@ -237,7 +237,7 @@ function PathCreation({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObject name={`${path.modeluuid}-vehicle-path`} key={path.modeluuid} ref={el => (groupRefs.current[path.modeluuid] = el!)} - position={path.assetPosition} + position={path.position} onClick={(e) => { if (isConnecting || eyeDropMode) return; e.stopPropagation(); diff --git a/app/src/services/factoryBuilder/assest/floorAsset/getFloorItemsApi.ts b/app/src/services/factoryBuilder/assest/floorAsset/getFloorItemsApi.ts index efe98fc..7a0290a 100644 --- a/app/src/services/factoryBuilder/assest/floorAsset/getFloorItemsApi.ts +++ b/app/src/services/factoryBuilder/assest/floorAsset/getFloorItemsApi.ts @@ -1,8 +1,8 @@ let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`; -export const getFloorItems = async (organization: string) => { +export const getFloorAssets = async (organization: string) => { try { - const response = await fetch(`${url_Backend_dwinzo}/api/v1/findfloorItems/${organization}`, { + const response = await fetch(`${url_Backend_dwinzo}/api/v2/floorAssets/${organization}`, { method: "GET", headers: { "Content-Type": "application/json", @@ -10,7 +10,7 @@ export const getFloorItems = async (organization: string) => { }); if (!response.ok) { - throw new Error("Failed to get Floor Items"); + throw new Error("Failed to get assets"); } const result = await response.json(); diff --git a/app/src/services/factoryBuilder/assest/floorAsset/setFloorItemApi.ts b/app/src/services/factoryBuilder/assest/floorAsset/setFloorItemApi.ts index f6cd496..75583cc 100644 --- a/app/src/services/factoryBuilder/assest/floorAsset/setFloorItemApi.ts +++ b/app/src/services/factoryBuilder/assest/floorAsset/setFloorItemApi.ts @@ -1,4 +1,4 @@ -let url_Backend_dwinzo = `http://${process.env.REACT_APP_TEST}`; +let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`; export const setFloorItemApi = async ( organization: string, diff --git a/app/src/services/simulation/getAssetEventType.ts b/app/src/services/simulation/getAssetEventType.ts index a681b12..84433ec 100644 --- a/app/src/services/simulation/getAssetEventType.ts +++ b/app/src/services/simulation/getAssetEventType.ts @@ -1,4 +1,4 @@ -let url_Backend_dwinzo = `http://${process.env.REACT_APP_TEST}`; +let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`; export const getAssetEventType = async (modelId: string, organization: string) => { try { diff --git a/app/src/types/world/worldTypes.d.ts b/app/src/types/world/worldTypes.d.ts index 4d10a36..d1ce13f 100644 --- a/app/src/types/world/worldTypes.d.ts +++ b/app/src/types/world/worldTypes.d.ts @@ -319,8 +319,8 @@ interface ConveyorEventsSchema { triggers: { uuid: string; name: string; type: string; isUsed: boolean; bufferTime: number }[] | []; connections: { source: { pathUUID: string; pointUUID: string }; targets: { pathUUID: string; pointUUID: string }[] }; }[]; - assetPosition: [number, number, number]; - assetRotation: [number, number, number]; + position: [number, number, number]; + rotation: [number, number, number]; speed: number | string; } @@ -335,5 +335,5 @@ interface VehicleEventsSchema { connections: { source: { pathUUID: string; pointUUID: string }; targets: { pathUUID: string; pointUUID: string }[] }; speed: number; }; - assetPosition: [number, number, number]; + position: [number, number, number]; } \ No newline at end of file From 03e7c32bfbd1e58881870430e7c1626a9c49f0c1 Mon Sep 17 00:00:00 2001 From: gabriel Date: Tue, 1 Apr 2025 14:27:19 +0530 Subject: [PATCH 03/12] Merge branch 'realTimeVisulization' of http://185.100.212.76:7776/Dwinzo-Beta/Dwinzo_dev into realTimeVisulization --- .../visualization/widgets/Widgets2D.tsx | 1 + .../IotInputCards/BarChartInput.tsx | 177 +++++++++++++ .../IotInputCards/FlotingWidgetInput.tsx | 19 +- .../IotInputCards/InputSelecterComponent.tsx | 75 ++++++ .../IotInputCards/LineGrapInput.tsx | 2 +- .../IotInputCards/PieChartInput.tsx | 241 ++++++++++++------ .../WarehouseThroughputInputComponent.tsx | 178 +++++++++++++ .../sidebarRight/visualization/data/Data.tsx | 4 +- .../ui/componets/DroppedFloatingWidgets.tsx | 3 +- .../realTimeVis/floating/FleetEfficiency.tsx | 4 +- .../floating/FleetEfficiencyComponent.tsx | 15 +- .../floating/WarehouseThroughputComponent.tsx | 117 +++++++-- app/src/store/store.ts | 2 +- app/src/store/useChartStore.ts | 20 +- app/src/styles/pages/realTimeViz.scss | 2 +- 15 files changed, 741 insertions(+), 119 deletions(-) create mode 100644 app/src/components/layout/sidebarRight/visualization/IotInputCards/BarChartInput.tsx create mode 100644 app/src/components/layout/sidebarRight/visualization/IotInputCards/InputSelecterComponent.tsx create mode 100644 app/src/components/layout/sidebarRight/visualization/IotInputCards/WarehouseThroughputInputComponent.tsx diff --git a/app/src/components/layout/sidebarLeft/visualization/widgets/Widgets2D.tsx b/app/src/components/layout/sidebarLeft/visualization/widgets/Widgets2D.tsx index 4c90582..c3696e0 100644 --- a/app/src/components/layout/sidebarLeft/visualization/widgets/Widgets2D.tsx +++ b/app/src/components/layout/sidebarLeft/visualization/widgets/Widgets2D.tsx @@ -99,6 +99,7 @@ const ProgressBarWidget = ({
); }; +console.log(chartTypes,"chartTypes"); const Widgets2D = () => { return ( diff --git a/app/src/components/layout/sidebarRight/visualization/IotInputCards/BarChartInput.tsx b/app/src/components/layout/sidebarRight/visualization/IotInputCards/BarChartInput.tsx new file mode 100644 index 0000000..8fe71a6 --- /dev/null +++ b/app/src/components/layout/sidebarRight/visualization/IotInputCards/BarChartInput.tsx @@ -0,0 +1,177 @@ +import React, { useEffect, useState } from "react"; +import MultiLevelDropdown from "../../../../ui/inputs/MultiLevelDropDown"; +import { AddIcon } from "../../../../icons/ExportCommonIcons"; +import RegularDropDown from "../../../../ui/inputs/RegularDropDown"; +import useChartStore from "../../../../../store/useChartStore"; +import { useSelectedZoneStore } from "../../../../../store/useZoneStore"; +import { useWidgetStore } from "../../../../../store/useWidgetStore"; +import axios from "axios"; +import RenameInput from "../../../../ui/inputs/RenameInput"; + +type Props = {}; + +const BarChartInput = (props: Props) => { + const [widgetName, setWidgetName] = useState('Widget'); + const { setMeasurements, updateDuration, updateName } = useChartStore(); + const [duration, setDuration] = useState('1h') + const [dropDowndata, setDropDownData] = useState({}); + const [selections, setSelections] = useState>({}); + const { selectedZone } = useSelectedZoneStore(); + const { selectedChartId } = useWidgetStore(); + const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL; + const email = localStorage.getItem("email") || ""; + const organization = email?.split("@")[1]?.split(".")[0] + + useEffect(() => { + const fetchZoneData = async () => { + try { + const response = await axios.get(`http://${iotApiUrl}/getinput`); + if (response.status === 200) { + // console.log("dropdown data:", response.data); + setDropDownData(response.data); + } else { + console.log("Unexpected response:", response); + } + } catch (error) { + console.error("There was an error!", error); + } + }; + fetchZoneData(); + }, []); + + useEffect(() => { + const fetchSavedInputes = async () => { + if (selectedChartId.id !== "") { + try { + const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/WidgetData/${selectedChartId.id}/${organization}`); + if (response.status === 200) { + setSelections(response.data.Data.measurements) + setDuration(response.data.Data.duration) + setWidgetName(response.data.widgetName) + } else { + console.log("Unexpected response:", response); + } + } catch (error) { + console.error("There was an error!", error); + } + } + } + + fetchSavedInputes(); + + }, [selectedChartId.id]); + + // Sync Zustand state when component mounts + useEffect(() => { + setMeasurements(selections); + updateDuration(duration); + updateName(widgetName); + }, [selections, duration, widgetName]); + + + const sendInputes = async (inputMeasurement: any, inputDuration: any, inputName: any) => { + try { + const response = await axios.post(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/widget/save`, { + organization: organization, + zoneId: selectedZone.zoneId, + widget: { + id: selectedChartId.id, + panel: selectedChartId.panel, + widgetName: inputName, + Data: { + measurements: inputMeasurement, + duration: inputDuration + } + } + } as any); + if (response.status === 200) { + return true + } else { + console.log("Unexpected response:", response); + return false + } + } catch (error) { + console.error("There was an error!", error); + return false + } + } + + const handleSelect = async (inputKey: string, selectedData: { name: string; fields: string } | null) => { + + // async() => { + const newSelections = { ...selections }; + if (selectedData === null) { + delete newSelections[inputKey]; + } else { + newSelections[inputKey] = selectedData; + } + // setMeasurements(newSelections); // Update Zustand store + console.log(newSelections); + if (await sendInputes(newSelections, duration, widgetName)) { + setSelections(newSelections); + } + // sendInputes(newSelections, duration); // Send data to server + // return newSelections; + // }; + }; + + const handleSelectDuration = async (option: string) => { + if (await sendInputes(selections, option, widgetName)) { + setDuration(option); + } + // setDuration(option); + }; + + const handleNameChange = async (name:any) => { + console.log('name change requested',name); + + if (await sendInputes(selections, duration, name)) { + setWidgetName(name); + } + } + + return ( + <> +
+
+
Title
+ +
+ {[...Array(3)].map((_, index) => { + const inputKey = `input${index + 1}`; + return ( +
+
Input {index + 1}
+
+ handleSelect(inputKey, selectedData)} + onUnselect={() => handleSelect(inputKey, null)} + selectedValue={selections[inputKey]} // Load from Zustand + /> +
+ +
+
+
+ ); + })} +
+
+
+
Duration
+
+ +
+
+
+ + ); +}; + +export default BarChartInput; \ No newline at end of file diff --git a/app/src/components/layout/sidebarRight/visualization/IotInputCards/FlotingWidgetInput.tsx b/app/src/components/layout/sidebarRight/visualization/IotInputCards/FlotingWidgetInput.tsx index 5938da9..0a58634 100644 --- a/app/src/components/layout/sidebarRight/visualization/IotInputCards/FlotingWidgetInput.tsx +++ b/app/src/components/layout/sidebarRight/visualization/IotInputCards/FlotingWidgetInput.tsx @@ -12,7 +12,7 @@ type Props = {}; const FlotingWidgetInput = (props: Props) => { const [widgetName, setWidgetName] = useState('Widget'); - const { setMeasurements, updateDuration, updateName } = useChartStore(); + const { setFlotingMeasurements, updateFlotingDuration, updateHeader } = useChartStore(); const [duration, setDuration] = useState('1h') const [dropDowndata, setDropDownData] = useState({}); const [selections, setSelections] = useState>({}); @@ -43,11 +43,13 @@ const FlotingWidgetInput = (props: Props) => { const fetchSavedInputes = async () => { if (selectedChartId.id !== "") { try { - const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/WidgetData/${selectedChartId.id}/${organization}`); + const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/A_floatWidget/${selectedChartId.id}/${organization}`); if (response.status === 200) { + console.log(response.data); + setSelections(response.data.Data.measurements) setDuration(response.data.Data.duration) - setWidgetName(response.data.widgetName) + setWidgetName(response.data.header) } else { console.log("Unexpected response:", response); } @@ -63,9 +65,9 @@ const FlotingWidgetInput = (props: Props) => { // Sync Zustand state when component mounts useEffect(() => { - setMeasurements(selections); - updateDuration(duration); - updateName(widgetName); + setFlotingMeasurements(selections); + updateFlotingDuration(duration); + updateHeader(widgetName); }, [selections, duration, widgetName]); @@ -76,8 +78,7 @@ const FlotingWidgetInput = (props: Props) => { zoneId: selectedZone.zoneId, widget: { id: selectedChartId.id, - panel: selectedChartId.panel, - widgetName: inputName, + header: inputName, Data: { measurements: inputMeasurement, duration: inputDuration @@ -135,7 +136,7 @@ const FlotingWidgetInput = (props: Props) => {
Title
- +
{[...Array(6)].map((_, index) => { const inputKey = `input${index + 1}`; diff --git a/app/src/components/layout/sidebarRight/visualization/IotInputCards/InputSelecterComponent.tsx b/app/src/components/layout/sidebarRight/visualization/IotInputCards/InputSelecterComponent.tsx new file mode 100644 index 0000000..45ce802 --- /dev/null +++ b/app/src/components/layout/sidebarRight/visualization/IotInputCards/InputSelecterComponent.tsx @@ -0,0 +1,75 @@ +import React from 'react' +import LineGrapInput from './LineGrapInput' +import BarChartInput from './BarChartInput' +import PieChartInput from './PieChartInput' +import FlotingWidgetInput from './FlotingWidgetInput' +import WarehouseThroughputInputComponent from './WarehouseThroughputInputComponent' +import { useWidgetStore } from '../../../../../store/useWidgetStore' + +const InputSelecterComponent = () => { + const { selectedChartId } = useWidgetStore(); + console.log('selectedChartId:',selectedChartId); + + + if (selectedChartId && selectedChartId.type && selectedChartId.type === 'bar' ) { + return ( + <> +
2D Widget Input
+ + + ) + } + + else if (selectedChartId && selectedChartId.type && selectedChartId.type === 'line' ) { + return ( + <> +
2D Widget Input
+ + + ) + } + + else if (selectedChartId && selectedChartId.type && selectedChartId.type === 'pie' ) { + return ( + <> +
2D Widget Input
+ + + ) + } + + else if (selectedChartId && selectedChartId.type && selectedChartId.type === 'doughnut' ) { + return ( + <> +
2D Widget Input
+ + + ) + } + + else if (selectedChartId && selectedChartId.type && selectedChartId.type === 'polarArea' ) { + return ( + <> +
2D Widget Input
+ + + ) + } + + else if (selectedChartId && selectedChartId.className && selectedChartId.className === 'warehouseThroughput floating' ) { + return ( + <> +
Floting Widget Input
+ + + ) + } + + else { + return ( +
No chart selected
+ ) + } +} + +export default InputSelecterComponent \ No newline at end of file diff --git a/app/src/components/layout/sidebarRight/visualization/IotInputCards/LineGrapInput.tsx b/app/src/components/layout/sidebarRight/visualization/IotInputCards/LineGrapInput.tsx index 32a5590..017dc6d 100644 --- a/app/src/components/layout/sidebarRight/visualization/IotInputCards/LineGrapInput.tsx +++ b/app/src/components/layout/sidebarRight/visualization/IotInputCards/LineGrapInput.tsx @@ -256,7 +256,7 @@ const LineGrapInput = (props: Props) => {
Title
- {[...Array(6)].map((_, index) => { + {[...Array(4)].map((_, index) => { const inputKey = `input${index + 1}`; return (
diff --git a/app/src/components/layout/sidebarRight/visualization/IotInputCards/PieChartInput.tsx b/app/src/components/layout/sidebarRight/visualization/IotInputCards/PieChartInput.tsx index 3087471..e3bed25 100644 --- a/app/src/components/layout/sidebarRight/visualization/IotInputCards/PieChartInput.tsx +++ b/app/src/components/layout/sidebarRight/visualization/IotInputCards/PieChartInput.tsx @@ -1,78 +1,177 @@ -import React, { useEffect, useState } from 'react' -import MultiLevelDropdown from '../../../../ui/inputs/MultiLevelDropDown' -import { AddIcon } from '../../../../icons/ExportCommonIcons' -import axios from 'axios' +import React, { useEffect, useState } from "react"; +import MultiLevelDropdown from "../../../../ui/inputs/MultiLevelDropDown"; +import { AddIcon } from "../../../../icons/ExportCommonIcons"; +import RegularDropDown from "../../../../ui/inputs/RegularDropDown"; +import useChartStore from "../../../../../store/useChartStore"; +import { useSelectedZoneStore } from "../../../../../store/useZoneStore"; +import { useWidgetStore } from "../../../../../store/useWidgetStore"; +import axios from "axios"; +import RenameInput from "../../../../ui/inputs/RenameInput"; -type Props = {} +type Props = {}; const PieChartInput = (props: Props) => { - const [dropDowndata, setDropDownData] = useState({}) - const [selections, setSelections] = useState>({}) - const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL; + const [widgetName, setWidgetName] = useState('Widget'); + const { setMeasurements, updateDuration, updateName } = useChartStore(); + const [duration, setDuration] = useState('1h') + const [dropDowndata, setDropDownData] = useState({}); + const [selections, setSelections] = useState>({}); + const { selectedZone } = useSelectedZoneStore(); + const { selectedChartId } = useWidgetStore(); + const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL; + const email = localStorage.getItem("email") || ""; + const organization = email?.split("@")[1]?.split(".")[0] - useEffect(() => { - const fetchZoneData = async () => { - try { - const response = await axios.get(`http://${iotApiUrl}/getinput`); - if (response.status === 200) { - console.log('dropdown data:', response.data); - setDropDownData(response.data) - } else { - console.log('Unexpected response:', response); - } - } catch (error) { - console.error('There was an error!', error); - } - }; - fetchZoneData(); - }, []); - - useEffect(() => {console.log(selections); - },[selections]) - - const handleSelect = (inputKey: string, selectedData: {name: string, fields: string} | null) => { - setSelections(prev => { - if (selectedData === null) { - const newSelections = {...prev}; - delete newSelections[inputKey]; - return newSelections; - } else { - return { - ...prev, - [inputKey]: selectedData - }; - } - }); + useEffect(() => { + const fetchZoneData = async () => { + try { + const response = await axios.get(`http://${iotApiUrl}/getinput`); + if (response.status === 200) { + // console.log("dropdown data:", response.data); + setDropDownData(response.data); + } else { + console.log("Unexpected response:", response); + } + } catch (error) { + console.error("There was an error!", error); + } }; + fetchZoneData(); + }, []); - return ( - <> -
- {[...Array(3)].map((_, index) => { - const inputKey = `input${index+1}`; - return ( -
-
Input {index+1}
-
- handleSelect(inputKey, selectedData)} - onUnselect={() => handleSelect(inputKey, null)} - selectedValue={selections[inputKey]} - /> -
- -
-
-
- ); - })} -
-
- -
- - ) -} + useEffect(() => { + const fetchSavedInputes = async () => { + if (selectedChartId.id !== "") { + try { + const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/WidgetData/${selectedChartId.id}/${organization}`); + if (response.status === 200) { + setSelections(response.data.Data.measurements) + setDuration(response.data.Data.duration) + setWidgetName(response.data.widgetName) + } else { + console.log("Unexpected response:", response); + } + } catch (error) { + console.error("There was an error!", error); + } + } + } -export default PieChartInput \ No newline at end of file + fetchSavedInputes(); + + }, [selectedChartId.id]); + + // Sync Zustand state when component mounts + useEffect(() => { + setMeasurements(selections); + updateDuration(duration); + updateName(widgetName); + }, [selections, duration, widgetName]); + + + const sendInputes = async (inputMeasurement: any, inputDuration: any, inputName: any) => { + try { + const response = await axios.post(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/widget/save`, { + organization: organization, + zoneId: selectedZone.zoneId, + widget: { + id: selectedChartId.id, + panel: selectedChartId.panel, + widgetName: inputName, + Data: { + measurements: inputMeasurement, + duration: inputDuration + } + } + } as any); + if (response.status === 200) { + return true + } else { + console.log("Unexpected response:", response); + return false + } + } catch (error) { + console.error("There was an error!", error); + return false + } + } + + const handleSelect = async (inputKey: string, selectedData: { name: string; fields: string } | null) => { + + // async() => { + const newSelections = { ...selections }; + if (selectedData === null) { + delete newSelections[inputKey]; + } else { + newSelections[inputKey] = selectedData; + } + // setMeasurements(newSelections); // Update Zustand store + console.log(newSelections); + if (await sendInputes(newSelections, duration, widgetName)) { + setSelections(newSelections); + } + // sendInputes(newSelections, duration); // Send data to server + // return newSelections; + // }; + }; + + const handleSelectDuration = async (option: string) => { + if (await sendInputes(selections, option, widgetName)) { + setDuration(option); + } + // setDuration(option); + }; + + const handleNameChange = async (name:any) => { + console.log('name change requested',name); + + if (await sendInputes(selections, duration, name)) { + setWidgetName(name); + } + } + + return ( + <> +
+
+
Title
+ +
+ {[...Array(2)].map((_, index) => { + const inputKey = `input${index + 1}`; + return ( +
+
Input {index + 1}
+
+ handleSelect(inputKey, selectedData)} + onUnselect={() => handleSelect(inputKey, null)} + selectedValue={selections[inputKey]} // Load from Zustand + /> +
+ +
+
+
+ ); + })} +
+
+
+
Duration
+
+ +
+
+
+ + ); +}; + +export default PieChartInput; \ No newline at end of file diff --git a/app/src/components/layout/sidebarRight/visualization/IotInputCards/WarehouseThroughputInputComponent.tsx b/app/src/components/layout/sidebarRight/visualization/IotInputCards/WarehouseThroughputInputComponent.tsx new file mode 100644 index 0000000..cd8cb96 --- /dev/null +++ b/app/src/components/layout/sidebarRight/visualization/IotInputCards/WarehouseThroughputInputComponent.tsx @@ -0,0 +1,178 @@ +import React, { useEffect, useState } from "react"; +import MultiLevelDropdown from "../../../../ui/inputs/MultiLevelDropDown"; +import { AddIcon } from "../../../../icons/ExportCommonIcons"; +import RegularDropDown from "../../../../ui/inputs/RegularDropDown"; +import useChartStore from "../../../../../store/useChartStore"; +import { useSelectedZoneStore } from "../../../../../store/useZoneStore"; +import { useWidgetStore } from "../../../../../store/useWidgetStore"; +import axios from "axios"; +import RenameInput from "../../../../ui/inputs/RenameInput"; + +type Props = {}; + +const WarehouseThroughputInputComponent = (props: Props) => { + const [widgetName, setWidgetName] = useState('Widget'); + const { setFlotingMeasurements, updateFlotingDuration, updateHeader } = useChartStore(); + const [duration, setDuration] = useState('1h') + const [dropDowndata, setDropDownData] = useState({}); + const [selections, setSelections] = useState>({}); + const { selectedZone } = useSelectedZoneStore(); + const { selectedChartId } = useWidgetStore(); + const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL; + const email = localStorage.getItem("email") || ""; + const organization = email?.split("@")[1]?.split(".")[0] + + useEffect(() => { + const fetchZoneData = async () => { + try { + const response = await axios.get(`http://${iotApiUrl}/getinput`); + if (response.status === 200) { + // console.log("dropdown data:", response.data); + setDropDownData(response.data); + } else { + console.log("Unexpected response:", response); + } + } catch (error) { + console.error("There was an error!", error); + } + }; + fetchZoneData(); + }, []); + + useEffect(() => { + const fetchSavedInputes = async () => { + if (selectedChartId.id !== "") { + try { + const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/A_floatWidget/${selectedChartId.id}/${organization}`); + if (response.status === 200) { + console.log(response.data); + + setSelections(response.data.Data.measurements) + setDuration(response.data.Data.duration) + setWidgetName(response.data.header) + } else { + console.log("Unexpected response:", response); + } + } catch (error) { + console.error("There was an error!", error); + } + } + } + + fetchSavedInputes(); + + }, [selectedChartId.id]); + + // Sync Zustand state when component mounts + useEffect(() => { + setFlotingMeasurements(selections); + updateFlotingDuration(duration); + updateHeader(widgetName); + }, [selections, duration, widgetName]); + + + const sendInputes = async (inputMeasurement: any, inputDuration: any, inputName: any) => { + try { + const response = await axios.post(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/floatwidget/save`, { + organization: organization, + zoneId: selectedZone.zoneId, + widget: { + id: selectedChartId.id, + header: inputName, + Data: { + measurements: inputMeasurement, + duration: inputDuration + } + } + } as any); + if (response.status === 200) { + return true + } else { + console.log("Unexpected response:", response); + return false + } + } catch (error) { + console.error("There was an error!", error); + return false + } + } + + const handleSelect = async (inputKey: string, selectedData: { name: string; fields: string } | null) => { + + // async() => { + const newSelections = { ...selections }; + if (selectedData === null) { + delete newSelections[inputKey]; + } else { + newSelections[inputKey] = selectedData; + } + // setMeasurements(newSelections); // Update Zustand store + console.log(newSelections); + if (await sendInputes(newSelections, duration, widgetName)) { + setSelections(newSelections); + } + // sendInputes(newSelections, duration); // Send data to server + // return newSelections; + // }; + }; + + const handleSelectDuration = async (option: string) => { + if (await sendInputes(selections, option, widgetName)) { + setDuration(option); + } + // setDuration(option); + }; + + const handleNameChange = async (name:any) => { + console.log('name change requested',name); + + if (await sendInputes(selections, duration, name)) { + setWidgetName(name); + } + } + + return ( + <> +
+
+
Title
+ +
+ {[...Array(1)].map((_, index) => { + const inputKey = `input${index + 1}`; + return ( +
+
Input {index + 1}
+
+ handleSelect(inputKey, selectedData)} + onUnselect={() => handleSelect(inputKey, null)} + selectedValue={selections[inputKey]} // Load from Zustand + /> +
+ +
+
+
+ ); + })} +
+
+
+
Duration
+
+ +
+
+
+ + ); +}; + +export default WarehouseThroughputInputComponent; diff --git a/app/src/components/layout/sidebarRight/visualization/data/Data.tsx b/app/src/components/layout/sidebarRight/visualization/data/Data.tsx index 2c9b5c6..1b9969c 100644 --- a/app/src/components/layout/sidebarRight/visualization/data/Data.tsx +++ b/app/src/components/layout/sidebarRight/visualization/data/Data.tsx @@ -4,6 +4,7 @@ import { AddIcon, RemoveIcon } from "../../../../icons/ExportCommonIcons"; import MultiLevelDropDown from "../../../../ui/inputs/MultiLevelDropDown"; import LineGrapInput from "../IotInputCards/LineGrapInput"; import RenameInput from "../../../../ui/inputs/RenameInput"; +import InputSelecterComponent from "../IotInputCards/InputSelecterComponent"; // Define the data structure for demonstration purposes const DATA_STRUCTURE = { @@ -133,8 +134,7 @@ const Data = () => { { chartDataGroups[selectedChartId?.id] && <> -
2D Widget Input
- + } diff --git a/app/src/components/ui/componets/DroppedFloatingWidgets.tsx b/app/src/components/ui/componets/DroppedFloatingWidgets.tsx index d7a6960..35278bb 100644 --- a/app/src/components/ui/componets/DroppedFloatingWidgets.tsx +++ b/app/src/components/ui/componets/DroppedFloatingWidgets.tsx @@ -379,14 +379,13 @@ const DroppedObjects: React.FC = () => { ) : obj.className === "warehouseThroughput floating" ? ( <> - + ) : obj.className === "fleetEfficiency floating" ? ( <> ) : null} - {renderObjectContent(obj)}
handleKebabClick(obj.id, event)} diff --git a/app/src/components/ui/realTimeVis/floating/FleetEfficiency.tsx b/app/src/components/ui/realTimeVis/floating/FleetEfficiency.tsx index 07e5f19..cabc3e7 100644 --- a/app/src/components/ui/realTimeVis/floating/FleetEfficiency.tsx +++ b/app/src/components/ui/realTimeVis/floating/FleetEfficiency.tsx @@ -1,8 +1,8 @@ const FleetEfficiency = () => { - const progress = 75; // Example progress value (0-100) + const progress = 50; // Example progress value (0-100) // Calculate the rotation angle for the progress bar - const rotationAngle = -90 + progress * 3.6; // Progress starts from the left (-90°) + const rotationAngle = 45 + progress * 1.8; const handleDragStart = (event: React.DragEvent) => { const rect = event.currentTarget.getBoundingClientRect(); // Get position diff --git a/app/src/components/ui/realTimeVis/floating/FleetEfficiencyComponent.tsx b/app/src/components/ui/realTimeVis/floating/FleetEfficiencyComponent.tsx index 9b2fd7f..e9221e8 100644 --- a/app/src/components/ui/realTimeVis/floating/FleetEfficiencyComponent.tsx +++ b/app/src/components/ui/realTimeVis/floating/FleetEfficiencyComponent.tsx @@ -1,10 +1,11 @@ -import React from 'react' +import React,{ useEffect, useState} from 'react' -type Props = {} +const FleetEfficiencyComponent = ({object}: any) => { + const [ progress, setProgress ] = useState(0) -const FleetEfficiencyComponent = ({ - object -}: any) => { + // Calculate the rotation angle for the progress bar + const rotationAngle = 45 + progress * 1.8; + return ( <>

Fleet Efficiency

@@ -13,7 +14,7 @@ const FleetEfficiencyComponent = ({
@@ -21,7 +22,7 @@ const FleetEfficiencyComponent = ({
0%
-
{object.per}%
+
{progress}%
Optimal
100% diff --git a/app/src/components/ui/realTimeVis/floating/WarehouseThroughputComponent.tsx b/app/src/components/ui/realTimeVis/floating/WarehouseThroughputComponent.tsx index 3aa8698..22f6529 100644 --- a/app/src/components/ui/realTimeVis/floating/WarehouseThroughputComponent.tsx +++ b/app/src/components/ui/realTimeVis/floating/WarehouseThroughputComponent.tsx @@ -1,6 +1,9 @@ -import React, { useState } from 'react' +import React, { useState, useEffect } from 'react' import { Line } from 'react-chartjs-2' +import useChartStore from '../../../../store/useChartStore'; +import { useWidgetStore } from '../../../../store/useWidgetStore'; import axios from 'axios'; +import io from "socket.io-client"; const WarehouseThroughputComponent = ({ object @@ -8,6 +11,17 @@ const WarehouseThroughputComponent = ({ const [measurements, setmeasurements] = useState({}); const [duration, setDuration] = useState("1h") + const [name, setName] = useState(object.header ? object.header : '') + const [chartData, setChartData] = useState<{ labels: string[]; datasets: any[] }>({ + labels: [], + datasets: [], + }); + const email = localStorage.getItem("email") || ""; + const organization = email?.split("@")[1]?.split(".")[0] + const { header, flotingDuration, flotingMeasurements } = useChartStore(); + const { selectedChartId } = useWidgetStore(); + + const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL; const lineGraphData = { labels: [ @@ -95,35 +109,94 @@ const WarehouseThroughputComponent = ({ }; - // const fetchSavedInputes = async() => { + useEffect(() => { + if (!iotApiUrl || !measurements || Object.keys(measurements).length === 0) return; + + const socket = io(`http://${iotApiUrl}`); + + const inputData = { + measurements, + duration, + interval: 1000, + }; + + + const startStream = () => { + socket.emit("lineInput", inputData); + }; + + socket.on("connect", startStream); + + socket.on("lineOutput", (response) => { + const responseData = response.data; + + // Extract timestamps and values + const labels = responseData.time; + const datasets = Object.keys(measurements).map((key) => { + const measurement = measurements[key]; + const datasetKey = `${measurement.name}.${measurement.fields}`; + return { + label: datasetKey, + data: responseData[datasetKey]?.values ?? [], + borderColor: "#6f42c1", // Use the desired color for the line (purple) + backgroundColor: "rgba(111, 66, 193, 0.2)", // Use a semi-transparent purple for the fill + borderWidth: 2, // Line thickness + fill: true, // Enable fill for this dataset + pointRadius: 0, // Remove dots at each data point + tension: 0.5, // Smooth interpolation for the line + }; + }); + + setChartData({ labels, datasets }); + }); + + return () => { + socket.off("lineOutput"); + socket.emit("stop_stream"); // Stop streaming when component unmounts + socket.disconnect(); + }; + }, [measurements, duration, iotApiUrl]); + + const fetchSavedInputes = async() => { + + if (object?.id !== "") { + try { + const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/A_floatWidget/${object?.id}/${organization}`); + if (response.status === 200) { + setmeasurements(response.data.Data.measurements) + setDuration(response.data.Data.duration) + setName(response.data.header) + } else { + console.log("Unexpected response:", response); + } + } catch (error) { + console.error("There was an error!", error); + } + } + } + + useEffect(() => { + fetchSavedInputes(); + }, []); + + useEffect(() => { + if (selectedChartId?.id === object?.id) { + fetchSavedInputes(); + } + } + ,[header, flotingDuration, flotingMeasurements]) - // if (object.id !== "") { - // try { - // const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_LOCAL_BASE_URL}/api/v2/WidgetData/${id}/${organization}`); - // if (response.status === 200) { - // setmeasurements(response.data.Data.measurements) - // setDuration(response.data.Data.duration) - // setName(response.data.widgetName) - // } else { - // console.log("Unexpected response:", response); - // } - // } catch (error) { - // console.error("There was an error!", error); - // } - // } - // } - return ( <>
-

Warehouse Throughput

-

+

{name}

+ {/*

(+5) more in 2025 -

+

*/}
- + 0 ? chartData : lineGraphData} options={lineGraphOptions} />
) diff --git a/app/src/store/store.ts b/app/src/store/store.ts index 6affa99..ef5c25f 100644 --- a/app/src/store/store.ts +++ b/app/src/store/store.ts @@ -11,7 +11,7 @@ export const useSocketStore = create((set: any, get: any) => ({ return; } - const socket = io(`http://${process.env.REACT_APP_SERVER_SOCKET_API_BASE_URL}/Builder`, { + const socket = io(`http://${process.env.REACT_APP_SERVER_SOCKET_API_BASE_URL}`, { reconnection: false, auth: { email, organization }, }); diff --git a/app/src/store/useChartStore.ts b/app/src/store/useChartStore.ts index 079b9ff..cdd8de0 100644 --- a/app/src/store/useChartStore.ts +++ b/app/src/store/useChartStore.ts @@ -10,9 +10,15 @@ interface MeasurementStore { interval: number; duration: string; name: string; + header: string; + flotingDuration: string; + flotingMeasurements: Record; // Change array to Record setMeasurements: (newMeasurements: Record) => void; updateDuration: (newDuration: string) => void; updateName: (newName: string) => void; + updateHeader: (newHeader: string) => void; + updateFlotingDuration: (newFlotingDuration: string) => void; + setFlotingMeasurements: (newFlotingMeasurements: Record) => void; } const useChartStore = create((set) => ({ @@ -20,6 +26,9 @@ const useChartStore = create((set) => ({ interval: 1000, duration: "1h", name:'', + header:'', + flotingDuration: "1h", + flotingMeasurements: {}, setMeasurements: (newMeasurements) => set(() => ({ measurements: newMeasurements })), @@ -28,7 +37,16 @@ const useChartStore = create((set) => ({ set(() => ({ duration: newDuration })), updateName: (newName) => - set(() => ({ duration: newName })), + set(() => ({ name: newName })), + + updateHeader: (newHeader) => + set(() => ({ header: newHeader })), + + updateFlotingDuration: (newFlotingDuration) => + set(() => ({ flotingDuration: newFlotingDuration })), + + setFlotingMeasurements: (newFlotingMeasurements) => + set(() => ({ flotingMeasurements: newFlotingMeasurements })), })); export default useChartStore; diff --git a/app/src/styles/pages/realTimeViz.scss b/app/src/styles/pages/realTimeViz.scss index d58f12d..4440e6d 100644 --- a/app/src/styles/pages/realTimeViz.scss +++ b/app/src/styles/pages/realTimeViz.scss @@ -196,7 +196,7 @@ border-radius: 8px; box-shadow: 0px 2px 6px 0px rgba(60, 60, 67, 0.1); padding: 6px 0; - background-color: white; + background-color: var(--background-color); position: relative; .kebab { From 652dee0c4bbc4fc3181ea1da797bdf491e4af1db Mon Sep 17 00:00:00 2001 From: Vishnu Date: Tue, 1 Apr 2025 17:46:55 +0530 Subject: [PATCH 04/12] Update background color to use CSS variable and remove unused theme toggle logic --- app/src/styles/base/base.scss | 2 +- app/src/utils/theme.ts | 9 --------- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/app/src/styles/base/base.scss b/app/src/styles/base/base.scss index 0dea569..e74c0ec 100644 --- a/app/src/styles/base/base.scss +++ b/app/src/styles/base/base.scss @@ -75,7 +75,7 @@ height: 100vh; // Full viewport height width: 100vw; // Full viewport width overflow: hidden; // Prevent scrollbars - background-color: #232323; + background-color: var(--background-color-gray); } // Root overlay styles diff --git a/app/src/utils/theme.ts b/app/src/utils/theme.ts index 03d2454..754904b 100644 --- a/app/src/utils/theme.ts +++ b/app/src/utils/theme.ts @@ -20,12 +20,3 @@ export function toggleTheme() { // Initialize theme on page load setTheme(); - -// Example: Call toggleTheme() when a button is clicked -const toggleSwitch: Element | null = document.querySelector('#theme-switch'); - -if (toggleSwitch) { - toggleSwitch.addEventListener('click', toggleTheme); -} else { - console.warn("Theme switch button not found!"); -} From d2be2094eb9c3bacca316b037cf09e966a03b911 Mon Sep 17 00:00:00 2001 From: Poovizhi99 Date: Tue, 1 Apr 2025 18:16:11 +0530 Subject: [PATCH 05/12] functionality for grid distance --- .../components/layout/sidebarLeft/Assets.tsx | 46 +++-- .../properties/GlobalProperties.tsx | 21 ++- .../templates/CollaborationPopup.tsx | 7 +- app/src/modules/market/Card.tsx | 1 - app/src/modules/market/MarketPlace.tsx | 1 - app/src/modules/scene/environment/ground.tsx | 64 +++++-- app/src/modules/scene/environment/shadow.tsx | 52 ++++-- app/src/store/store.ts | 26 ++- app/src/styles/layout/sidebar.scss | 2 - app/src/types/world/worldConstants.ts | 164 +++++++++--------- 10 files changed, 247 insertions(+), 137 deletions(-) diff --git a/app/src/components/layout/sidebarLeft/Assets.tsx b/app/src/components/layout/sidebarLeft/Assets.tsx index 3e2510e..c924b0c 100644 --- a/app/src/components/layout/sidebarLeft/Assets.tsx +++ b/app/src/components/layout/sidebarLeft/Assets.tsx @@ -24,26 +24,42 @@ interface AssetProp { price?: number; CreatedBy?: String; } +interface CategoryListProp { + assetImage?: string; + assetName?: string; + categoryImage: string; + category: string; +} const Assets: React.FC = () => { const { setSelectedItem } = useSelectedItem(); const [searchValue, setSearchValue] = useState(""); const [selectedCategory, setSelectedCategory] = useState(null); const [categoryAssets, setCategoryAssets] = useState([]); const [filtereredAssets, setFiltereredAssets] = useState([]); + const [categoryList, setCategoryList] = useState([]); const handleSearchChange = (value: string) => { + const searchTerm = value.toLowerCase(); setSearchValue(value); - setSelectedCategory(null); - const searchTerm = value.toLowerCase(); // Convert input to lowercase + if (searchTerm.trim() === "" && !selectedCategory) { + setCategoryAssets([]); + return; + } const filteredModels = filtereredAssets?.filter((model) => { - if (!model?.tags || !model?.filename) return false; + 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 (!searchTerm.startsWith(":")) { + } else if (selectedCategory) { + return ( + model.category + .toLowerCase() + .includes(selectedCategory.toLowerCase()) && + model.filename.toLowerCase().includes(searchTerm) + ); + } else { return model.filename.toLowerCase().includes(searchTerm); } - return false; }); setCategoryAssets(filteredModels); @@ -56,10 +72,10 @@ const Assets: React.FC = () => { } catch {} }; filteredAssets(); - }, []); + }, [categoryAssets]); - const categoryList = useMemo( - () => [ + useEffect(() => { + setCategoryList([ { assetName: "Doors", assetImage: "", @@ -82,10 +98,8 @@ const Assets: React.FC = () => { { category: "Workstation", categoryImage: workStation }, { category: "Machines", categoryImage: machines }, { category: "Workers", categoryImage: worker }, - ], - [] - ); - + ]); + }, []); const fetchCategoryAssets = async (asset: any) => { setSelectedCategory(asset); if (asset === "Feneration") { @@ -127,7 +141,7 @@ const Assets: React.FC = () => {
-

Results for "{searchValue}"

+

Results for {searchValue}

@@ -155,10 +169,12 @@ const Assets: React.FC = () => {
) : selectedCategory ? (
- {/* Back Button */}
setSelectedCategory(null)} + onClick={() => { + setSelectedCategory(null); + setCategoryAssets([]); + }} > ← Back
diff --git a/app/src/components/layout/sidebarRight/properties/GlobalProperties.tsx b/app/src/components/layout/sidebarRight/properties/GlobalProperties.tsx index d65a10c..4e6a537 100644 --- a/app/src/components/layout/sidebarRight/properties/GlobalProperties.tsx +++ b/app/src/components/layout/sidebarRight/properties/GlobalProperties.tsx @@ -13,6 +13,7 @@ import { useSelectedWallItem, useShadows, useSocketStore, + useTileDistance, useToggleView, useWallVisibility, } from "../../../../store/store"; @@ -29,14 +30,18 @@ const GlobalProperties: React.FC = () => { const { elevation, setElevation } = useElevation(); const { azimuth, setAzimuth } = useAzimuth(); const { renderDistance, setRenderDistance } = useRenderDistance(); - + const { setPlaneValue, setGridValue, planeValue, gridValue } = + useTileDistance(); + useEffect(() => { + console.log(gridValue, planeValue, "values"); + }, [gridValue, planeValue]); const { socket } = useSocketStore(); const { limitDistance, setLimitDistance } = useLimitDistance(); const [distance, setDistance] = useState(40); useEffect(() => {}, [limitDistance]); const [limitGridDistance, setLimitGridDistance] = useState(false); - const [gridDistance, setGridDistance] = useState(5); + const [gridDistance, setGridDistance] = useState(3); const optimizeScene = async (value: any) => { const email = localStorage.getItem("email"); @@ -89,7 +94,15 @@ const GlobalProperties: React.FC = () => { } function updateGridDistance(value: number) { setGridDistance(value); + // setGridValue({ size: value * 100, divisions: (value * 100) / 4 }); + // setPlaneValue({ height: value * 100, width: value * 100 }); } + function updatedGrid(value: number) { + console.log(" (value * 100) / 4 : ", (value * 100) / 4); + setGridValue({ size: value * 100, divisions: (value * 100) / 4 }); + setPlaneValue({ height: value * 100, width: value * 100 }); + } + const updatedDist = async (value: number) => { const email = localStorage.getItem("email"); const organization = email?.split("@")[1]?.split(".")[0] || "defaultOrg"; @@ -266,6 +279,7 @@ const GlobalProperties: React.FC = () => { max={CONSTANTS.distanceConfig.maxDistance} onChange={(value: number) => updateDistance(value)} onPointerUp={updatedDist} + key={"6"} />
@@ -283,7 +297,10 @@ const GlobalProperties: React.FC = () => { disabled={!limitGridDistance} value={gridDistance} key={"7"} + min={1} + max={5} onChange={(value: number) => updateGridDistance(value)} + onPointerUp={updatedGrid} />
); diff --git a/app/src/components/templates/CollaborationPopup.tsx b/app/src/components/templates/CollaborationPopup.tsx index 8312fe7..d2e1c16 100644 --- a/app/src/components/templates/CollaborationPopup.tsx +++ b/app/src/components/templates/CollaborationPopup.tsx @@ -1,10 +1,11 @@ -import React, { useState } from "react"; +import React, { useEffect, useState } from "react"; import RenderOverlay from "./Overlay"; import { ArrowIcon, CloseIcon } from "../icons/ExportCommonIcons"; import { AccessOption, User } from "../../types/users"; import RegularDropDown from "../ui/inputs/RegularDropDown"; import { access } from "fs"; import MultiEmailInvite from "../ui/inputs/MultiEmailInvite"; +import { useActiveUsers } from "../../store/store"; interface UserListTemplateProps { user: User; @@ -57,6 +58,10 @@ interface CollaborateProps { const CollaborationPopup: React.FC = ({ setUserManagement, }) => { + const { activeUsers } = useActiveUsers(); + useEffect(() => { + console.log("activeUsers: ", activeUsers); + }, [activeUsers]); const userName = localStorage.getItem("userName") || "Anonymous"; const users = [ { diff --git a/app/src/modules/market/Card.tsx b/app/src/modules/market/Card.tsx index c1a3289..ec9db06 100644 --- a/app/src/modules/market/Card.tsx +++ b/app/src/modules/market/Card.tsx @@ -38,7 +38,6 @@ const Card: React.FC = ({ description, onSelectCard, }) => { - console.log('description: ', description); const handleCardSelect = () => { onSelectCard({ assetName, uploadedOn, price, rating, views, description }); }; diff --git a/app/src/modules/market/MarketPlace.tsx b/app/src/modules/market/MarketPlace.tsx index cd2e0e9..dfcf62e 100644 --- a/app/src/modules/market/MarketPlace.tsx +++ b/app/src/modules/market/MarketPlace.tsx @@ -25,7 +25,6 @@ const MarketPlace = () => { const filteredAssets = async () => { try { const filt = await getAssetImages("67d934ad0f42a1fdadb19aa6"); - setModels(filt.items); setFilteredModels(filt.items); } catch {} diff --git a/app/src/modules/scene/environment/ground.tsx b/app/src/modules/scene/environment/ground.tsx index ebc017e..8d2fa22 100644 --- a/app/src/modules/scene/environment/ground.tsx +++ b/app/src/modules/scene/environment/ground.tsx @@ -1,22 +1,52 @@ -import { useToggleView } from '../../../store/store'; -import * as CONSTANTS from '../../../types/world/worldConstants'; +import { useTileDistance, useToggleView } from "../../../store/store"; +import * as CONSTANTS from "../../../types/world/worldConstants"; const Ground = ({ grid, plane }: any) => { - const { toggleView } = useToggleView(); - const savedTheme: string | null = localStorage.getItem('theme'); + const { toggleView } = useToggleView(); + const savedTheme: string | null = localStorage.getItem("theme"); + const { planeValue, gridValue } = useTileDistance(); - return ( - - - - - - - - - - - ) -} + return ( + + + + + + + + + + ); +}; export default Ground; diff --git a/app/src/modules/scene/environment/shadow.tsx b/app/src/modules/scene/environment/shadow.tsx index 62202b3..4625cde 100644 --- a/app/src/modules/scene/environment/shadow.tsx +++ b/app/src/modules/scene/environment/shadow.tsx @@ -1,9 +1,22 @@ -import { useRef, useEffect} from 'react'; -import { useThree } from '@react-three/fiber'; -import * as THREE from 'three'; -import { useAzimuth, useElevation, useShadows, useSunPosition, useFloorItems, useWallItems } from '../../../store/store'; -import * as CONSTANTS from '../../../types/world/worldConstants'; -const shadowWorker = new Worker(new URL('../../../services/factoryBuilder/webWorkers/shadowWorker', import.meta.url)); +import { useRef, useEffect } from "react"; +import { useThree } from "@react-three/fiber"; +import * as THREE from "three"; +import { + useAzimuth, + useElevation, + useShadows, + useSunPosition, + useFloorItems, + useWallItems, + useTileDistance, +} from "../../../store/store"; +import * as CONSTANTS from "../../../types/world/worldConstants"; +const shadowWorker = new Worker( + new URL( + "../../../services/factoryBuilder/webWorkers/shadowWorker", + import.meta.url + ) +); export default function Shadows() { const { shadows, setShadows } = useShadows(); @@ -15,6 +28,7 @@ export default function Shadows() { const { azimuth, setAzimuth } = useAzimuth(); const { floorItems } = useFloorItems(); const { wallItems } = useWallItems(); + const { planeValue } = useTileDistance(); useEffect(() => { gl.shadowMap.enabled = true; @@ -48,9 +62,9 @@ export default function Shadows() { useEffect(() => { if (controls && shadows) { updateShadows(); - (controls as any).addEventListener('update', updateShadows); + (controls as any).addEventListener("update", updateShadows); return () => { - (controls as any).removeEventListener('update', updateShadows); + (controls as any).removeEventListener("update", updateShadows); }; } }, [controls, elevation, azimuth, shadows]); @@ -75,10 +89,24 @@ export default function Shadows() { shadow-normalBias={CONSTANTS.shadowConfig.shadownormalBias} /> - - - + + {/* + */} + + ); -} \ No newline at end of file +} diff --git a/app/src/store/store.ts b/app/src/store/store.ts index d111aae..9de1f39 100644 --- a/app/src/store/store.ts +++ b/app/src/store/store.ts @@ -11,10 +11,13 @@ export const useSocketStore = create((set: any, get: any) => ({ return; } - const socket = io(`http://${process.env.REACT_APP_SERVER_SOCKET_API_BASE_URL}/Builder`, { - reconnection: false, - auth: { email, organization }, - }); + const socket = io( + `http://${process.env.REACT_APP_SERVER_SOCKET_API_BASE_URL}/Builder`, + { + reconnection: false, + auth: { email, organization }, + } + ); set({ socket }); }, @@ -401,3 +404,18 @@ export const useLimitDistance = create((set: any) => ({ limitDistance: true, setLimitDistance: (x: any) => set({ limitDistance: x }), })); + +export const useTileDistance = create((set: any) => ({ + gridValue: { size: 300, divisions: 75 }, + planeValue: { height: 300, width: 300 }, + + setGridValue: (value: any) => + set((state: any) => ({ + gridValue: { ...state.gridValue, ...value }, + })), + + setPlaneValue: (value: any) => + set((state: any) => ({ + planeValue: { ...state.planeValue, ...value }, + })), +})); diff --git a/app/src/styles/layout/sidebar.scss b/app/src/styles/layout/sidebar.scss index 92b4eb2..63297c9 100644 --- a/app/src/styles/layout/sidebar.scss +++ b/app/src/styles/layout/sidebar.scss @@ -1052,7 +1052,6 @@ position: relative; z-index: 3; font-size: var(--font-size-regular); - color: var(--background-color); } .asset-image { @@ -1103,7 +1102,6 @@ position: relative; z-index: 3; font-size: var(--font-size-regular); - color: var(--background-color); } .asset-image { diff --git a/app/src/types/world/worldConstants.ts b/app/src/types/world/worldConstants.ts index 2bc3835..fe0aafb 100644 --- a/app/src/types/world/worldConstants.ts +++ b/app/src/types/world/worldConstants.ts @@ -59,39 +59,39 @@ export type ThreeDimension = { }; export type GridConfig = { - size: number; - divisions: number; - primaryColor: string; - secondaryColor: string; - position2D: [x: number, y: number, z: number]; - position3D: [x: number, y: number, z: number]; -} + size: number; + divisions: number; + primaryColor: string; + secondaryColor: string; + position2D: [x: number, y: number, z: number]; + position3D: [x: number, y: number, z: number]; +}; export type PlaneConfig = { - position2D: [x: number, y: number, z: number]; - position3D: [x: number, y: number, z: number]; - rotation: number; - width: number; - height: number; - color: string; -} + position2D: [x: number, y: number, z: number]; + position3D: [x: number, y: number, z: number]; + rotation: number; + width: number; + height: number; + color: string; +}; export type ShadowConfig = { - shadowOffset: number, - shadowmapSizewidth: number, - shadowmapSizeheight: number, - shadowcamerafar: number, - shadowcameranear: number, - shadowcameratop: number, - shadowcamerabottom: number, - shadowcameraleft: number, - shadowcameraright: number, - shadowbias: number, - shadownormalBias: number, - shadowMaterialPosition: [x: number, y: number, z: number], - shadowMaterialRotation: [x: number, y: number, z: number], - shadowMaterialOpacity: number, -} + shadowOffset: number; + shadowmapSizewidth: number; + shadowmapSizeheight: number; + shadowcamerafar: number; + shadowcameranear: number; + shadowcameratop: number; + shadowcamerabottom: number; + shadowcameraleft: number; + shadowcameraright: number; + shadowbias: number; + shadownormalBias: number; + shadowMaterialPosition: [x: number, y: number, z: number]; + shadowMaterialRotation: [x: number, y: number, z: number]; + shadowMaterialOpacity: number; +}; export type SkyConfig = { defaultTurbidity: number; @@ -109,34 +109,34 @@ export type AssetConfig = { }; export type PointConfig = { - defaultInnerColor: string; - defaultOuterColor: string; - deleteColor: string; - boxScale: [number, number, number]; - wallOuterColor: string; - floorOuterColor: string; - aisleOuterColor: string; - zoneOuterColor: string; - snappingThreshold: number; -} + defaultInnerColor: string; + defaultOuterColor: string; + deleteColor: string; + boxScale: [number, number, number]; + wallOuterColor: string; + floorOuterColor: string; + aisleOuterColor: string; + zoneOuterColor: string; + snappingThreshold: number; +}; export type LineConfig = { - tubularSegments: number; - radius: number; - radialSegments: number; - wallName: string; - floorName: string; - aisleName: string; - zoneName: string; - referenceName: string; - lineIntersectionPoints: number; - defaultColor: string; - wallColor: string; - floorColor: string; - aisleColor: string; - zoneColor: string; - helperColor: string; -} + tubularSegments: number; + radius: number; + radialSegments: number; + wallName: string; + floorName: string; + aisleName: string; + zoneName: string; + referenceName: string; + lineIntersectionPoints: number; + defaultColor: string; + wallColor: string; + floorColor: string; + aisleColor: string; + zoneColor: string; + helperColor: string; +}; export type WallConfig = { defaultColor: string; @@ -145,10 +145,10 @@ export type WallConfig = { }; export type FloorConfig = { - defaultColor: string; - height: number; - textureScale: number; -} + defaultColor: string; + height: number; + textureScale: number; +}; export type RoofConfig = { defaultColor: string; @@ -156,16 +156,16 @@ export type RoofConfig = { }; export type AisleConfig = { - width: number; - height: number; - defaultColor: number; -} + width: number; + height: number; + defaultColor: number; +}; export type ZoneConfig = { - defaultColor: string; - height: number; - color: string; -} + defaultColor: string; + height: number; + color: string; +}; export type ColumnConfig = { defaultColor: string; @@ -242,24 +242,24 @@ export const threeDimension: ThreeDimension = { export const camPositionUpdateInterval: number = 200; // Interval for updating the camera position export const gridConfig: GridConfig = { - size: 300, // Size of the grid - divisions: 75, // Number of divisions in the grid - primaryColor: savedTheme === "dark" ? "#131313" : "#d5d5d5", // Primary color of the grid - secondaryColor: savedTheme === "dark" ? "#434343" : "#e3e3e3", // Secondary color of the grid + size: 300, // Size of the grid + divisions: 75, // Number of divisions in the grid + primaryColor: savedTheme === "dark" ? "#131313" : "#d5d5d5", // Primary color of the grid + secondaryColor: savedTheme === "dark" ? "#434343" : "#e3e3e3", // Secondary color of the grid - position2D: [0, 0.1, 0], // Position of the grid in 2D view - position3D: [0, -0.5, 0], // Position of the grid in 3D view -} + position2D: [0, 0.1, 0], // Position of the grid in 2D view + position3D: [0, -0.5, 0], // Position of the grid in 3D view +}; export const planeConfig: PlaneConfig = { position2D: [0, -0.5, 0], // Position of the plane position3D: [0, -0.65, 0], // Position of the plane rotation: -Math.PI / 2, // Rotation of the plane - width: 300, // Width of the plane - height: 300, // Height of the plane - color: savedTheme === "dark" ? "#323232" : "#f3f3f3" // Color of the plane -} + width: 300, // Width of the plane + height: 300, // Height of the plane + color: savedTheme === "dark" ? "#323232" : "#f3f3f3", // Color of the plane +}; export const shadowConfig: ShadowConfig = { shadowOffset: 50, // Offset of the shadow @@ -349,10 +349,10 @@ export const aisleConfig: AisleConfig = { }; export const zoneConfig: ZoneConfig = { - defaultColor: "black", // Default color of the zones - height: 3, - color: "#8656DF" // Color of the zones -} + defaultColor: "black", // Default color of the zones + height: 3, + color: "#8656DF", // Color of the zones +}; export const columnConfig: ColumnConfig = { defaultColor: "White", // Default color of the columns From 2043712f5d3df4542d9d122bd642221db5dbfc93 Mon Sep 17 00:00:00 2001 From: Jerald-Golden-B Date: Tue, 1 Apr 2025 18:52:21 +0530 Subject: [PATCH 06/12] add backend api and socket for conveyor events --- app/.env | 7 +++- .../sidebarRight/visualization/data/Data.tsx | 2 +- .../geomentries/assets/addAssetModel.ts | 1 - .../builder/groups/floorItemsGroup.tsx | 3 +- .../scene/IntialLoad/loadInitialFloorItems.ts | 5 +-- .../simulation/behaviour/behaviour.tsx | 41 ++----------------- app/src/modules/simulation/simulation.tsx | 1 + .../assest/assets/getCategoryAsset.ts | 2 +- 8 files changed, 15 insertions(+), 47 deletions(-) diff --git a/app/.env b/app/.env index e0b0d16..5740621 100644 --- a/app/.env +++ b/app/.env @@ -7,8 +7,11 @@ REACT_APP_SERVER_SOCKET_API_BASE_URL=185.100.212.76:8000 # Base URL for the server REST API, used for HTTP requests to the backend server. REACT_APP_SERVER_REST_API_BASE_URL=185.100.212.76:5000 -# REACT_APP_SERVER_MARKETPLACE_URL=185.100.212.76:50011 -REACT_APP_SERVER_MARKETPLACE_URL=192.168.0.111:3501 +# Base URL for the server marketplace, used for market place model blob. +REACT_APP_SERVER_MARKETPLACE_URL=185.100.212.76:50011 + +# Base URL for the asset library server, used for asset library images and model blob id. +REACT_APP_SERVER_ASSET_LIBRARY_URL=192.168.0.111:3501 # base url for IoT socket server REACT_APP_IOT_SOCKET_SERVER_URL =185.100.212.76:5010 diff --git a/app/src/components/layout/sidebarRight/visualization/data/Data.tsx b/app/src/components/layout/sidebarRight/visualization/data/Data.tsx index 2c9b5c6..5f501a2 100644 --- a/app/src/components/layout/sidebarRight/visualization/data/Data.tsx +++ b/app/src/components/layout/sidebarRight/visualization/data/Data.tsx @@ -118,7 +118,7 @@ const Data = () => { }; }); }; - console.log("selectedChartId", selectedChartId); + // console.log("selectedChartId", selectedChartId); return (
diff --git a/app/src/modules/builder/geomentries/assets/addAssetModel.ts b/app/src/modules/builder/geomentries/assets/addAssetModel.ts index 0cf9f22..a4255bd 100644 --- a/app/src/modules/builder/geomentries/assets/addAssetModel.ts +++ b/app/src/modules/builder/geomentries/assets/addAssetModel.ts @@ -148,7 +148,6 @@ async function handleModelLoad( const organization = email ? email.split("@")[1].split(".")[0] : ""; getAssetEventType(selectedItem.id, organization).then(async (res) => { - console.log('res: ', res); if (res.type === "Conveyor") { const pointUUIDs = res.points.map(() => THREE.MathUtils.generateUUID()); diff --git a/app/src/modules/builder/groups/floorItemsGroup.tsx b/app/src/modules/builder/groups/floorItemsGroup.tsx index 7e7282b..2f2241e 100644 --- a/app/src/modules/builder/groups/floorItemsGroup.tsx +++ b/app/src/modules/builder/groups/floorItemsGroup.tsx @@ -306,8 +306,7 @@ const FloorItemsGroup = ({ itemsGroup, hoveredDeletableFloorItem, AttachedObject }, [deleteModels, transformMode, controls, selectedItem, state.camera, state.pointer, activeTool, activeModule]); useEffect(() => { - - console.log('floorItems: ', floorItems); + // console.log('floorItems: ', floorItems); }, [floorItems]) useFrame(() => { diff --git a/app/src/modules/scene/IntialLoad/loadInitialFloorItems.ts b/app/src/modules/scene/IntialLoad/loadInitialFloorItems.ts index 2b1b1f3..dfff78d 100644 --- a/app/src/modules/scene/IntialLoad/loadInitialFloorItems.ts +++ b/app/src/modules/scene/IntialLoad/loadInitialFloorItems.ts @@ -19,7 +19,6 @@ async function loadInitialFloorItems( const organization = (email!.split("@")[1]).split(".")[0]; const items = await getFloorAssets(organization); - console.log('items: ', items); localStorage.setItem("FloorItems", JSON.stringify(items)); await initializeDB(); @@ -133,7 +132,7 @@ async function loadInitialFloorItems( modelfileID: item.modelfileID, isLocked: item.isLocked, isVisible: item.isVisible, - eventData: item.eventData, + // eventData: item.eventData, }, ]); } else { @@ -198,7 +197,7 @@ function processLoadedModel( modelfileID: item.modelfileID, isLocked: item.isLocked, isVisible: item.isVisible, - eventData: item.eventData, + // eventData: item.eventData, }, ]); } else { diff --git a/app/src/modules/simulation/behaviour/behaviour.tsx b/app/src/modules/simulation/behaviour/behaviour.tsx index 3cb1df1..53af231 100644 --- a/app/src/modules/simulation/behaviour/behaviour.tsx +++ b/app/src/modules/simulation/behaviour/behaviour.tsx @@ -11,48 +11,16 @@ function Behaviour() { const newPaths: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema)[] = []; floorItems.forEach((item: Types.FloorItemType) => { - if (item.modelfileID === "672a090f80d91ac979f4d0bd") { - const point1Position = new THREE.Vector3(0, 0.85, 2.2); - const middlePointPosition = new THREE.Vector3(0, 0.85, 0); - const point2Position = new THREE.Vector3(0, 0.85, -2.2); - - const point1UUID = THREE.MathUtils.generateUUID(); - const middlePointUUID = THREE.MathUtils.generateUUID(); - const point2UUID = THREE.MathUtils.generateUUID(); - + // console.log('item: ', item); + if (item.eventData && item.eventData.type === 'Conveyor') { const newPath: Types.ConveyorEventsSchema = { modeluuid: item.modeluuid, modelName: item.modelname, type: 'Conveyor', - points: [ - { - uuid: point1UUID, - position: [point1Position.x, point1Position.y, point1Position.z], - rotation: [0, 0, 0], - actions: [{ uuid: THREE.MathUtils.generateUUID(), name: 'Action 1', type: 'Inherit', material: 'Inherit', delay: 'Inherit', spawnInterval: 'Inherit', isUsed: false }], - triggers: [], - connections: { source: { pathUUID: item.modeluuid, pointUUID: point1UUID }, targets: [] }, - }, - { - uuid: middlePointUUID, - position: [middlePointPosition.x, middlePointPosition.y, middlePointPosition.z], - rotation: [0, 0, 0], - actions: [{ uuid: THREE.MathUtils.generateUUID(), name: 'Action 1', type: 'Inherit', material: 'Inherit', delay: 'Inherit', spawnInterval: 'Inherit', isUsed: false }], - triggers: [], - connections: { source: { pathUUID: item.modeluuid, pointUUID: middlePointUUID }, targets: [] }, - }, - { - uuid: point2UUID, - position: [point2Position.x, point2Position.y, point2Position.z], - rotation: [0, 0, 0], - actions: [{ uuid: THREE.MathUtils.generateUUID(), name: 'Action 1', type: 'Inherit', material: 'Inherit', delay: 'Inherit', spawnInterval: 'Inherit', isUsed: false }], - triggers: [], - connections: { source: { pathUUID: item.modeluuid, pointUUID: point2UUID }, targets: [] }, - }, - ], + points: item.eventData.points, position: [...item.position], rotation: [item.rotation.x, item.rotation.y, item.rotation.z], - speed: 'Inherit', + speed: item.eventData.speed, }; newPaths.push(newPath); @@ -80,7 +48,6 @@ function Behaviour() { setSimulationPaths(newPaths); }, [floorItems]); - return null; } diff --git a/app/src/modules/simulation/simulation.tsx b/app/src/modules/simulation/simulation.tsx index 739a92b..2d72a40 100644 --- a/app/src/modules/simulation/simulation.tsx +++ b/app/src/modules/simulation/simulation.tsx @@ -13,6 +13,7 @@ function Simulation() { const [processes, setProcesses] = useState([]); useEffect(() => { + // console.log('simulationPaths: ', simulationPaths); }, [simulationPaths]); // useEffect(() => { diff --git a/app/src/services/factoryBuilder/assest/assets/getCategoryAsset.ts b/app/src/services/factoryBuilder/assest/assets/getCategoryAsset.ts index 522e54c..a1ac727 100644 --- a/app/src/services/factoryBuilder/assest/assets/getCategoryAsset.ts +++ b/app/src/services/factoryBuilder/assest/assets/getCategoryAsset.ts @@ -1,4 +1,4 @@ -let BackEnd_url = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`; +let BackEnd_url = `http://${process.env.REACT_APP_SERVER_ASSET_LIBRARY_URL}`; export const getCategoryAsset = async (categoryName: any) => { try { const response = await fetch( From 8a177478c671a23fb3fc6cdff027e13c03c48a28 Mon Sep 17 00:00:00 2001 From: SreeNath14 <153710861+SreeNath14@users.noreply.github.com> Date: Tue, 1 Apr 2025 18:54:04 +0530 Subject: [PATCH 07/12] "updated animation" --- .../simulation/process/processAnimator.tsx | 416 ++++++++++++++++++ .../simulation/process/processContainer.tsx | 17 + .../simulation/process/processCreator.tsx | 398 +++++++++++++++++ app/src/modules/simulation/simulation.tsx | 2 + 4 files changed, 833 insertions(+) create mode 100644 app/src/modules/simulation/process/processAnimator.tsx create mode 100644 app/src/modules/simulation/process/processContainer.tsx create mode 100644 app/src/modules/simulation/process/processCreator.tsx diff --git a/app/src/modules/simulation/process/processAnimator.tsx b/app/src/modules/simulation/process/processAnimator.tsx new file mode 100644 index 0000000..123ec80 --- /dev/null +++ b/app/src/modules/simulation/process/processAnimator.tsx @@ -0,0 +1,416 @@ +import React, { useRef, useState, useEffect, useMemo } from "react"; +import { usePlayButtonStore } from "../../../store/usePlayButtonStore"; +import { GLTFLoader } from "three-stdlib"; +import { useLoader, useFrame } from "@react-three/fiber"; +import * as THREE from "three"; +import { GLTF } from "three-stdlib"; +import boxGltb from "../../../assets/gltf-glb/crate_box.glb"; + +interface PointAction { + uuid: string; + name: string; + type: "Inherit" | "Spawn" | "Despawn" | "Delay" | "Swap"; + material: string; + delay: string | number; + spawnInterval: string | number; + isUsed: boolean; +} + +interface ProcessPoint { + uuid: string; + position: number[]; + rotation: number[]; + actions: PointAction[]; + connections: { + source: { pathUUID: string; pointUUID: string }; + targets: { pathUUID: string; pointUUID: string }[]; + }; +} + +interface ProcessPath { + modeluuid: string; + modelName: string; + points: ProcessPoint[]; + pathPosition: number[]; + pathRotation: number[]; + speed: number; +} + +interface ProcessData { + id: string; + paths: ProcessPath[]; + animationPath: { x: number; y: number; z: number }[]; + pointActions: PointAction[][]; + speed: number; +} + +interface AnimationState { + currentIndex: number; + progress: number; + isAnimating: boolean; + speed: number; + isDelaying: boolean; + delayStartTime: number; + currentDelayDuration: number; + delayComplete: boolean; + currentPathIndex: number; + spawnPoints: Record< + string, + { + position: THREE.Vector3; + interval: number; + lastSpawnTime: number; + } + >; +} + +const MAX_SPAWNED_OBJECTS = 20; + +const ProcessAnimator: React.FC<{ processes: ProcessData[] }> = ({ + processes, +}) => { + console.log("processes: ", processes); + const gltf = useLoader(GLTFLoader, boxGltb) as GLTF; + const { isPlaying, setIsPlaying } = usePlayButtonStore(); + + const groupRef = useRef(null); + const meshRef = useRef(null); + const [visible, setVisible] = useState(false); + const spawnedObjectsRef = useRef([]); + + const materials = useMemo( + () => ({ + Wood: new THREE.MeshStandardMaterial({ color: 0x8b4513 }), + Box: new THREE.MeshStandardMaterial({ + color: 0xcccccc, + metalness: 0.8, + roughness: 0.2, + }), + Crate: new THREE.MeshStandardMaterial({ + color: 0x00aaff, + metalness: 0.1, + roughness: 0.5, + }), + Default: new THREE.MeshStandardMaterial({ color: 0x00ff00 }), + }), + [] + ); + + const [currentMaterial, setCurrentMaterial] = useState( + materials.Default + ); + + const { animationPath, currentProcess } = useMemo(() => { + const defaultProcess = { + animationPath: [], + pointActions: [], + speed: 1, + paths: [], + }; + const cp = processes?.[0] || defaultProcess; + return { + animationPath: + cp.animationPath?.map((p) => new THREE.Vector3(p.x, p.y, p.z)) || [], + currentProcess: cp, + }; + }, [processes]); + + const animationStateRef = useRef({ + currentIndex: 0, + progress: 0, + isAnimating: false, + speed: currentProcess.speed, + isDelaying: false, + delayStartTime: 0, + currentDelayDuration: 0, + delayComplete: false, + currentPathIndex: 0, + spawnPoints: {}, + }); + + const getPointDataForAnimationIndex = (index: number) => { + if (!processes[0]?.paths) return null; + + if (index < 3) { + return processes[0].paths[0]?.points[index]; + } else { + const path2Index = index - 3; + return processes[0].paths[1]?.points[path2Index]; + } + }; + + useEffect(() => { + if (isPlaying) { + setVisible(true); + animationStateRef.current = { + currentIndex: 0, + progress: 0, + isAnimating: true, + speed: currentProcess.speed, + isDelaying: false, + delayStartTime: 0, + currentDelayDuration: 0, + delayComplete: false, + currentPathIndex: 0, + spawnPoints: {}, + }; + + // Clear spawned objects + if (groupRef.current) { + spawnedObjectsRef.current.forEach((obj) => { + if (groupRef.current?.children.includes(obj)) { + groupRef.current.remove(obj); + } + if (obj instanceof THREE.Mesh) { + obj.material.dispose(); + } + }); + spawnedObjectsRef.current = []; + } + + const currentRef = gltf?.scene ? groupRef.current : meshRef.current; + if (currentRef && animationPath.length > 0) { + currentRef.position.copy(animationPath[0]); + } + } else { + animationStateRef.current.isAnimating = false; + } + }, [isPlaying, currentProcess, animationPath]); + + const handleMaterialSwap = (materialType: string) => { + const newMaterial = + materials[materialType as keyof typeof materials] || materials.Default; + setCurrentMaterial(newMaterial); + + spawnedObjectsRef.current.forEach((obj) => { + if (obj instanceof THREE.Mesh) { + obj.material = newMaterial.clone(); + } + }); + }; + + const hasNonInheritActions = (actions: PointAction[] = []) => { + return actions.some((action) => action.isUsed && action.type !== "Inherit"); + }; + + const handlePointActions = ( + actions: PointAction[] = [], + currentTime: number, + currentPosition: THREE.Vector3 + ) => { + let shouldStopAnimation = false; + + actions.forEach((action) => { + if (!action.isUsed) return; + + switch (action.type) { + case "Delay": + if ( + !animationStateRef.current.isDelaying && + !animationStateRef.current.delayComplete + ) { + const delayDuration = + typeof action.delay === "number" + ? action.delay + : parseFloat(action.delay as string) || 0; + + if (delayDuration > 0) { + animationStateRef.current.isDelaying = true; + animationStateRef.current.delayStartTime = currentTime; + animationStateRef.current.currentDelayDuration = delayDuration; + shouldStopAnimation = true; + } + } + break; + + case "Despawn": + setVisible(false); + setIsPlaying(false); + animationStateRef.current.isAnimating = false; + shouldStopAnimation = true; + break; + + case "Spawn": + const spawnInterval = + typeof action.spawnInterval === "number" + ? action.spawnInterval + : parseFloat(action.spawnInterval as string) || 1; + + const positionKey = currentPosition.toArray().join(","); + + animationStateRef.current.spawnPoints[positionKey] = { + position: currentPosition.clone(), + interval: spawnInterval, + lastSpawnTime: currentTime - spawnInterval, // Force immediate spawn + }; + break; + + case "Swap": + if (action.material) { + handleMaterialSwap(action.material); + } + break; + + case "Inherit": + break; + } + }); + + return shouldStopAnimation; + }; + + useFrame((state, delta) => { + const currentRef = gltf?.scene ? groupRef.current : meshRef.current; + if ( + !currentRef || + !animationStateRef.current.isAnimating || + animationPath.length < 2 + ) { + return; + } + + const currentTime = state.clock.getElapsedTime(); + const path = animationPath; + const stateRef = animationStateRef.current; + + if (stateRef.currentIndex === 3 && stateRef.currentPathIndex === 0) { + stateRef.currentPathIndex = 1; + } + + const currentPointData = getPointDataForAnimationIndex( + stateRef.currentIndex + ); + + if (stateRef.progress === 0 && currentPointData?.actions) { + const shouldStop = handlePointActions( + currentPointData.actions, + currentTime, + currentRef.position + ); + if (shouldStop) return; + } + + if (stateRef.isDelaying) { + if ( + currentTime - stateRef.delayStartTime >= + stateRef.currentDelayDuration + ) { + stateRef.isDelaying = false; + stateRef.delayComplete = true; + } else { + return; + } + } + + // Handle spawning - this is the key updated part + Object.entries(stateRef.spawnPoints).forEach(([key, spawnPoint]) => { + if (currentTime - spawnPoint.lastSpawnTime >= spawnPoint.interval) { + spawnPoint.lastSpawnTime = currentTime; + + if (gltf?.scene && groupRef?.current) { + const newObject = gltf.scene.clone(); + newObject.position.copy(spawnPoint.position); + + newObject.traverse((child) => { + if (child instanceof THREE.Mesh) { + child.material = currentMaterial.clone(); + } + }); + + groupRef.current.add(newObject); + spawnedObjectsRef.current.push(newObject); + + // Clean up old objects if needed + console.log( + "spawnedObjectsRef.current.length: ", + spawnedObjectsRef.current.length + ); + if (spawnedObjectsRef.current.length > MAX_SPAWNED_OBJECTS) { + const oldest = spawnedObjectsRef.current.shift(); + if (oldest && groupRef.current.children.includes(oldest)) { + groupRef.current.remove(oldest); + if (oldest instanceof THREE.Mesh) { + oldest.material.dispose(); + } + } + } + } + } + }); + + const nextPointIdx = stateRef.currentIndex + 1; + const isLastPoint = nextPointIdx >= path.length; + + if (isLastPoint) { + if (currentPointData?.actions) { + const shouldStop = !hasNonInheritActions(currentPointData.actions); + if (shouldStop) { + currentRef.position.copy(path[stateRef.currentIndex]); + setIsPlaying(false); + stateRef.isAnimating = false; + return; + } + } + } + + if (!isLastPoint) { + const nextPoint = path[nextPointIdx]; + const distance = path[stateRef.currentIndex].distanceTo(nextPoint); + const movement = stateRef.speed * delta; + stateRef.progress += movement / distance; + + if (stateRef.progress >= 1) { + stateRef.currentIndex = nextPointIdx; + stateRef.progress = 0; + stateRef.delayComplete = false; + currentRef.position.copy(nextPoint); + } else { + currentRef.position.lerpVectors( + path[stateRef.currentIndex], + nextPoint, + stateRef.progress + ); + } + } + }); + + useEffect(() => { + return () => { + if (groupRef.current) { + spawnedObjectsRef.current.forEach((obj) => { + if (groupRef.current?.children.includes(obj)) { + groupRef.current.remove(obj); + } + if (obj instanceof THREE.Mesh) { + obj.material.dispose(); + } + }); + spawnedObjectsRef.current = []; + } + }; + }, []); + + if (!processes || processes.length === 0) { + return null; + } + + if (!gltf?.scene) { + return visible ? ( + + + + ) : null; + } + + return visible ? ( + + + + ) : null; +}; + +export default ProcessAnimator; diff --git a/app/src/modules/simulation/process/processContainer.tsx b/app/src/modules/simulation/process/processContainer.tsx new file mode 100644 index 0000000..967376c --- /dev/null +++ b/app/src/modules/simulation/process/processContainer.tsx @@ -0,0 +1,17 @@ +import React, { useState } from "react"; +import ProcessCreator from "./processCreator"; +import ProcessAnimator from "./processAnimator"; + +const ProcessContainer: React.FC = () => { + const [processes, setProcesses] = useState([]); + + + return ( + <> + + {processes.length > 0 && } + + ); +}; + +export default ProcessContainer; diff --git a/app/src/modules/simulation/process/processCreator.tsx b/app/src/modules/simulation/process/processCreator.tsx new file mode 100644 index 0000000..8e1ed1d --- /dev/null +++ b/app/src/modules/simulation/process/processCreator.tsx @@ -0,0 +1,398 @@ +import React, { + useEffect, + useMemo, + useState, + useCallback, + useRef, +} from "react"; +import { useSimulationPaths } from "../../../store/store"; +import * as THREE from "three"; +import { useThree } from "@react-three/fiber"; +import { + ConveyorEventsSchema, + VehicleEventsSchema, +} from "../../../types/world/worldTypes"; + +// Type definitions +export interface PointAction { + uuid: string; + name: string; + type: string; + material: string; + delay: number | string; + spawnInterval: string | number; + isUsed: boolean; +} + +export interface PathPoint { + uuid: string; + position: [number, number, number]; + actions: PointAction[]; + connections: { + targets: Array<{ pathUUID: string }>; + }; +} + +export interface SimulationPath { + modeluuid: string; + points: PathPoint[]; + pathPosition: [number, number, number]; + speed?: number; +} + +export interface Process { + id: string; + paths: SimulationPath[]; + animationPath: THREE.Vector3[]; + pointActions: PointAction[][]; + speed: number; +} + +interface ProcessCreatorProps { + onProcessesCreated: (processes: Process[]) => void; +} + +// Convert event schemas to SimulationPath +function convertToSimulationPath( + path: ConveyorEventsSchema | VehicleEventsSchema +): SimulationPath { + const { modeluuid } = path; + + // Simplified normalizeAction function that preserves exact original properties + const normalizeAction = (action: any): PointAction => { + return { ...action }; // Return exact copy with no modifications + }; + + if (path.type === "Conveyor") { + return { + modeluuid, + points: path.points.map((point) => ({ + uuid: point.uuid, + position: point.position, + actions: point.actions.map(normalizeAction), // Preserve exact actions + connections: { + targets: point.connections.targets.map((target) => ({ + pathUUID: target.pathUUID, + })), + }, + })), + pathPosition: path.position, + speed: + typeof path.speed === "string" + ? parseFloat(path.speed) || 1 + : path.speed || 1, + }; + } else { + return { + modeluuid, + points: [ + { + uuid: path.point.uuid, + position: path.point.position, + actions: Array.isArray(path.point.actions) + ? path.point.actions.map(normalizeAction) + : [normalizeAction(path.point.actions)], + connections: { + targets: path.point.connections.targets.map((target) => ({ + pathUUID: target.pathUUID, + })), + }, + }, + ], + pathPosition: path.position, + speed: path.point.speed || 1, + }; + } +} + +// Custom shallow comparison for arrays +const areArraysEqual = (a: any[], b: any[]) => { + if (a.length !== b.length) return false; + for (let i = 0; i < a.length; i++) { + if (a[i] !== b[i]) return false; + } + return true; +}; + +// Helper function to create an empty process +const createEmptyProcess = (): Process => ({ + id: `process-${Math.random().toString(36).substring(2, 11)}`, + paths: [], + animationPath: [], + pointActions: [], + speed: 1, +}); + +// Enhanced connection checking function +function shouldReverseNextPath( + currentPath: SimulationPath, + nextPath: SimulationPath +): boolean { + if (nextPath.points.length !== 3) return false; + + const currentLastPoint = currentPath.points[currentPath.points.length - 1]; + const nextFirstPoint = nextPath.points[0]; + const nextLastPoint = nextPath.points[nextPath.points.length - 1]; + + // Check if current last connects to next last (requires reversal) + const connectsToLast = currentLastPoint.connections.targets.some( + (target) => + target.pathUUID === nextPath.modeluuid && + nextLastPoint.connections.targets.some( + (t) => t.pathUUID === currentPath.modeluuid + ) + ); + + // Check if current last connects to next first (no reversal needed) + const connectsToFirst = currentLastPoint.connections.targets.some( + (target) => + target.pathUUID === nextPath.modeluuid && + nextFirstPoint.connections.targets.some( + (t) => t.pathUUID === currentPath.modeluuid + ) + ); + + // Only reverse if connected to last point and not to first point + return connectsToLast && !connectsToFirst; +} + +// Updated path adjustment function +function adjustPathPointsOrder(paths: SimulationPath[]): SimulationPath[] { + if (paths.length < 2) return paths; + + const adjustedPaths = [...paths]; + + for (let i = 0; i < adjustedPaths.length - 1; i++) { + const currentPath = adjustedPaths[i]; + const nextPath = adjustedPaths[i + 1]; + + if (shouldReverseNextPath(currentPath, nextPath)) { + const reversedPoints = [ + nextPath.points[2], + nextPath.points[1], + nextPath.points[0], + ]; + + adjustedPaths[i + 1] = { + ...nextPath, + points: reversedPoints, + }; + } + } + + return adjustedPaths; +} + +// Main hook for process creation +export function useProcessCreation() { + const { scene } = useThree(); + const [processes, setProcesses] = useState([]); + + const hasSpawnAction = useCallback((path: SimulationPath): boolean => { + return path.points.some((point) => + point.actions.some((action) => action.type.toLowerCase() === "spawn") + ); + }, []); + + const createProcess = useCallback( + (paths: SimulationPath[]): Process => { + if (!paths || paths.length === 0) { + return createEmptyProcess(); + } + + const animationPath: THREE.Vector3[] = []; + const pointActions: PointAction[][] = []; + const processSpeed = paths[0]?.speed || 1; + + for (const path of paths) { + for (const point of path.points) { + const obj = scene.getObjectByProperty("uuid", point.uuid); + if (!obj) { + console.warn(`Object with UUID ${point.uuid} not found in scene`); + continue; + } + + const position = obj.getWorldPosition(new THREE.Vector3()); + animationPath.push(position.clone()); + pointActions.push(point.actions); + } + } + + return { + id: `process-${Math.random().toString(36).substring(2, 11)}`, + paths, + animationPath, + pointActions, + speed: processSpeed, + }; + }, + [scene] + ); + + const getAllConnectedPaths = useCallback( + ( + initialPath: SimulationPath, + allPaths: SimulationPath[], + visited: Set = new Set() + ): SimulationPath[] => { + const connectedPaths: SimulationPath[] = []; + const queue: SimulationPath[] = [initialPath]; + visited.add(initialPath.modeluuid); + + const pathMap = new Map(); + allPaths.forEach((path) => pathMap.set(path.modeluuid, path)); + + while (queue.length > 0) { + const currentPath = queue.shift()!; + connectedPaths.push(currentPath); + + // Process outgoing connections + for (const point of currentPath.points) { + for (const target of point.connections.targets) { + if (!visited.has(target.pathUUID)) { + const targetPath = pathMap.get(target.pathUUID); + if (targetPath) { + visited.add(target.pathUUID); + queue.push(targetPath); + } + } + } + } + + // Process incoming connections + for (const [uuid, path] of pathMap) { + if (!visited.has(uuid)) { + const hasConnectionToCurrent = path.points.some((point) => + point.connections.targets.some( + (t) => t.pathUUID === currentPath.modeluuid + ) + ); + if (hasConnectionToCurrent) { + visited.add(uuid); + queue.push(path); + } + } + } + } + + return connectedPaths; + }, + [] + ); + + const createProcessesFromPaths = useCallback( + (paths: SimulationPath[]): Process[] => { + if (!paths || paths.length === 0) return []; + + const visited = new Set(); + const processes: Process[] = []; + const pathMap = new Map(); + paths.forEach((path) => pathMap.set(path.modeluuid, path)); + + for (const path of paths) { + if (!visited.has(path.modeluuid) && hasSpawnAction(path)) { + const connectedPaths = getAllConnectedPaths(path, paths, visited); + const adjustedPaths = adjustPathPointsOrder(connectedPaths); + const process = createProcess(adjustedPaths); + processes.push(process); + } + } + + return processes; + }, + [createProcess, getAllConnectedPaths, hasSpawnAction] + ); + + return { + processes, + createProcessesFromPaths, + setProcesses, + }; +} + +const ProcessCreator: React.FC = React.memo( + ({ onProcessesCreated }) => { + const { simulationPaths } = useSimulationPaths(); + const { createProcessesFromPaths } = useProcessCreation(); + const prevPathsRef = useRef([]); + const prevProcessesRef = useRef([]); + + const convertedPaths = useMemo((): SimulationPath[] => { + if (!simulationPaths) return []; + return simulationPaths.map((path) => + convertToSimulationPath( + path as ConveyorEventsSchema | VehicleEventsSchema + ) + ); + }, [simulationPaths]); + + const pathsDependency = useMemo(() => { + if (!convertedPaths) return null; + return convertedPaths.map((path) => ({ + id: path.modeluuid, + hasSpawn: path.points.some((p: PathPoint) => + p.actions.some((a: PointAction) => a.type.toLowerCase() === "spawn") + ), + connections: path.points + .flatMap((p: PathPoint) => + p.connections.targets.map((t: { pathUUID: string }) => t.pathUUID) + ) + .join(","), + })); + }, [convertedPaths]); + + useEffect(() => { + if (!convertedPaths || convertedPaths.length === 0) { + if (prevProcessesRef.current.length > 0) { + onProcessesCreated([]); + prevProcessesRef.current = []; + } + return; + } + + if (areArraysEqual(prevPathsRef.current, convertedPaths)) { + return; + } + + prevPathsRef.current = convertedPaths; + const newProcesses = createProcessesFromPaths(convertedPaths); + + // console.log("--- Action Types in Paths ---"); + // convertedPaths.forEach((path) => { + // path.points.forEach((point) => { + // point.actions.forEach((action) => { + // console.log( + // `Path ${path.modeluuid}, Point ${point.uuid}: ${action.type}` + // ); + // }); + // }); + // }); + // console.log("New processes:", newProcesses); + + if ( + newProcesses.length !== prevProcessesRef.current.length || + !newProcesses.every( + (proc, i) => + proc.paths.length === prevProcessesRef.current[i]?.paths.length && + proc.paths.every( + (path, j) => + path.modeluuid === + prevProcessesRef.current[i]?.paths[j]?.modeluuid + ) + ) + ) { + onProcessesCreated(newProcesses); + // prevProcessesRef.current = newProcesses; + } + }, [ + pathsDependency, + onProcessesCreated, + convertedPaths, + createProcessesFromPaths, + ]); + + return null; + } +); + +export default ProcessCreator; diff --git a/app/src/modules/simulation/simulation.tsx b/app/src/modules/simulation/simulation.tsx index 739a92b..6fb0036 100644 --- a/app/src/modules/simulation/simulation.tsx +++ b/app/src/modules/simulation/simulation.tsx @@ -5,6 +5,7 @@ import Behaviour from './behaviour/behaviour'; import PathCreation from './path/pathCreation'; import PathConnector from './path/pathConnector'; import useModuleStore from '../../store/useModuleStore'; +import ProcessContainer from './process/processContainer'; function Simulation() { const { activeModule } = useModuleStore(); @@ -35,6 +36,7 @@ function Simulation() { <> + )} From 00ea0432b5f48818a8e661773e3f535b791ed1bb Mon Sep 17 00:00:00 2001 From: Jerald-Golden-B Date: Tue, 1 Apr 2025 19:04:15 +0530 Subject: [PATCH 08/12] feat: implement conveyor path creation with dynamic points and actions --- .../simulation/behaviour/behaviour.tsx | 42 +++++++++++++++++-- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/app/src/modules/simulation/behaviour/behaviour.tsx b/app/src/modules/simulation/behaviour/behaviour.tsx index 53af231..d127705 100644 --- a/app/src/modules/simulation/behaviour/behaviour.tsx +++ b/app/src/modules/simulation/behaviour/behaviour.tsx @@ -11,16 +11,49 @@ function Behaviour() { const newPaths: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema)[] = []; floorItems.forEach((item: Types.FloorItemType) => { - // console.log('item: ', item); - if (item.eventData && item.eventData.type === 'Conveyor') { + if (item.modelfileID === "672a090f80d91ac979f4d0bd") { + console.log('item: ', item); + const point1Position = new THREE.Vector3(0, 0.85, 2.2); + const middlePointPosition = new THREE.Vector3(0, 0.85, 0); + const point2Position = new THREE.Vector3(0, 0.85, -2.2); + + const point1UUID = THREE.MathUtils.generateUUID(); + const middlePointUUID = THREE.MathUtils.generateUUID(); + const point2UUID = THREE.MathUtils.generateUUID(); + const newPath: Types.ConveyorEventsSchema = { modeluuid: item.modeluuid, modelName: item.modelname, type: 'Conveyor', - points: item.eventData.points, + points: [ + { + uuid: point1UUID, + position: [point1Position.x, point1Position.y, point1Position.z], + rotation: [0, 0, 0], + actions: [{ uuid: THREE.MathUtils.generateUUID(), name: 'Action 1', type: 'Inherit', material: 'Inherit', delay: 'Inherit', spawnInterval: 'Inherit', isUsed: true }], + triggers: [], + connections: { source: { pathUUID: item.modeluuid, pointUUID: point1UUID }, targets: [] }, + }, + { + uuid: middlePointUUID, + position: [middlePointPosition.x, middlePointPosition.y, middlePointPosition.z], + rotation: [0, 0, 0], + actions: [{ uuid: THREE.MathUtils.generateUUID(), name: 'Action 1', type: 'Inherit', material: 'Inherit', delay: 'Inherit', spawnInterval: 'Inherit', isUsed: true }], + triggers: [], + connections: { source: { pathUUID: item.modeluuid, pointUUID: middlePointUUID }, targets: [] }, + }, + { + uuid: point2UUID, + position: [point2Position.x, point2Position.y, point2Position.z], + rotation: [0, 0, 0], + actions: [{ uuid: THREE.MathUtils.generateUUID(), name: 'Action 1', type: 'Inherit', material: 'Inherit', delay: 'Inherit', spawnInterval: 'Inherit', isUsed: true }], + triggers: [], + connections: { source: { pathUUID: item.modeluuid, pointUUID: point2UUID }, targets: [] }, + }, + ], position: [...item.position], rotation: [item.rotation.x, item.rotation.y, item.rotation.z], - speed: item.eventData.speed, + speed: 'Inherit', }; newPaths.push(newPath); @@ -48,6 +81,7 @@ function Behaviour() { setSimulationPaths(newPaths); }, [floorItems]); + return null; } From 688b7eb4a0c7f5f6198fd0fded20050b395a0e21 Mon Sep 17 00:00:00 2001 From: gabriel Date: Tue, 1 Apr 2025 19:06:29 +0530 Subject: [PATCH 09/12] Add iot data and custom input card for floting cards --- .../visualization/widgets/Widgets2D.tsx | 8 +- .../FleetEfficiencyInputComponent.tsx | 178 ++++++++++++++++++ .../IotInputCards/InputSelecterComponent.tsx | 39 ++++ .../IotInputCards/Progress1Input.tsx | 171 +++++++++++++++++ .../IotInputCards/Progress2Input.tsx | 171 +++++++++++++++++ .../components/ui/componets/DisplayZone.tsx | 5 +- .../ui/componets/DraggableWidget.tsx | 36 +--- .../ui/componets/Dropped3dWidget.tsx | 1 + .../ui/realTimeVis/charts/ProgressCard1.tsx | 105 +++++++++++ .../ui/realTimeVis/charts/ProgressCard2.tsx | 125 ++++++++++++ .../floating/FleetEfficiencyComponent.tsx | 83 +++++++- .../floating/TotalCardComponent.tsx | 92 ++++++++- app/src/styles/pages/realTimeViz.scss | 7 +- 13 files changed, 976 insertions(+), 45 deletions(-) create mode 100644 app/src/components/layout/sidebarRight/visualization/IotInputCards/FleetEfficiencyInputComponent.tsx create mode 100644 app/src/components/layout/sidebarRight/visualization/IotInputCards/Progress1Input.tsx create mode 100644 app/src/components/layout/sidebarRight/visualization/IotInputCards/Progress2Input.tsx create mode 100644 app/src/components/ui/realTimeVis/charts/ProgressCard1.tsx create mode 100644 app/src/components/ui/realTimeVis/charts/ProgressCard2.tsx diff --git a/app/src/components/layout/sidebarLeft/visualization/widgets/Widgets2D.tsx b/app/src/components/layout/sidebarLeft/visualization/widgets/Widgets2D.tsx index c3696e0..561c82b 100644 --- a/app/src/components/layout/sidebarLeft/visualization/widgets/Widgets2D.tsx +++ b/app/src/components/layout/sidebarLeft/visualization/widgets/Widgets2D.tsx @@ -59,9 +59,11 @@ const ProgressBarWidget = ({ id, title, data, + type }: { id: string; title: string; + type: string; data: any; }) => { const { setDraggedAsset } = useWidgetStore((state) => state); @@ -72,7 +74,7 @@ const ProgressBarWidget = ({ draggable onDragStart={() => { setDraggedAsset({ - type: "progress", + type: type, id, title, panel: "top", @@ -99,7 +101,7 @@ const ProgressBarWidget = ({
); }; -console.log(chartTypes,"chartTypes"); +console.log(chartTypes, "chartTypes"); const Widgets2D = () => { return ( @@ -124,6 +126,7 @@ const Widgets2D = () => { { key: "units", value: 1000, description: "Initial stock" }, ], }} + type={"progress 1"} /> { { key: "units", value: 500, description: "Additional stock" }, ], }} + type={"progress 2"} />
diff --git a/app/src/components/layout/sidebarRight/visualization/IotInputCards/FleetEfficiencyInputComponent.tsx b/app/src/components/layout/sidebarRight/visualization/IotInputCards/FleetEfficiencyInputComponent.tsx new file mode 100644 index 0000000..3a86ea9 --- /dev/null +++ b/app/src/components/layout/sidebarRight/visualization/IotInputCards/FleetEfficiencyInputComponent.tsx @@ -0,0 +1,178 @@ +import React, { useEffect, useState } from "react"; +import MultiLevelDropdown from "../../../../ui/inputs/MultiLevelDropDown"; +import { AddIcon } from "../../../../icons/ExportCommonIcons"; +import RegularDropDown from "../../../../ui/inputs/RegularDropDown"; +import useChartStore from "../../../../../store/useChartStore"; +import { useSelectedZoneStore } from "../../../../../store/useZoneStore"; +import { useWidgetStore } from "../../../../../store/useWidgetStore"; +import axios from "axios"; +import RenameInput from "../../../../ui/inputs/RenameInput"; + +type Props = {}; + +const FleetEfficiencyInputComponent = (props: Props) => { + const [widgetName, setWidgetName] = useState('Widget'); + const { setFlotingMeasurements, updateFlotingDuration, updateHeader } = useChartStore(); + const [duration, setDuration] = useState('1h') + const [dropDowndata, setDropDownData] = useState({}); + const [selections, setSelections] = useState>({}); + const { selectedZone } = useSelectedZoneStore(); + const { selectedChartId } = useWidgetStore(); + const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL; + const email = localStorage.getItem("email") || ""; + const organization = email?.split("@")[1]?.split(".")[0] + + useEffect(() => { + const fetchZoneData = async () => { + try { + const response = await axios.get(`http://${iotApiUrl}/getinput`); + if (response.status === 200) { + // console.log("dropdown data:", response.data); + setDropDownData(response.data); + } else { + console.log("Unexpected response:", response); + } + } catch (error) { + console.error("There was an error!", error); + } + }; + fetchZoneData(); + }, []); + + useEffect(() => { + const fetchSavedInputes = async () => { + if (selectedChartId.id !== "") { + try { + const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/A_floatWidget/${selectedChartId.id}/${organization}`); + if (response.status === 200) { + console.log(response.data); + + setSelections(response.data.Data.measurements) + setDuration(response.data.Data.duration) + setWidgetName(response.data.header) + } else { + console.log("Unexpected response:", response); + } + } catch (error) { + console.error("There was an error!", error); + } + } + } + + fetchSavedInputes(); + + }, [selectedChartId.id]); + + // Sync Zustand state when component mounts + useEffect(() => { + setFlotingMeasurements(selections); + updateFlotingDuration(duration); + updateHeader(widgetName); + }, [selections, duration, widgetName]); + + + const sendInputes = async (inputMeasurement: any, inputDuration: any, inputName: any) => { + try { + const response = await axios.post(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/floatwidget/save`, { + organization: organization, + zoneId: selectedZone.zoneId, + widget: { + id: selectedChartId.id, + header: inputName, + Data: { + measurements: inputMeasurement, + duration: inputDuration + } + } + } as any); + if (response.status === 200) { + return true + } else { + console.log("Unexpected response:", response); + return false + } + } catch (error) { + console.error("There was an error!", error); + return false + } + } + + const handleSelect = async (inputKey: string, selectedData: { name: string; fields: string } | null) => { + + // async() => { + const newSelections = { ...selections }; + if (selectedData === null) { + delete newSelections[inputKey]; + } else { + newSelections[inputKey] = selectedData; + } + // setMeasurements(newSelections); // Update Zustand store + console.log(newSelections); + if (await sendInputes(newSelections, duration, widgetName)) { + setSelections(newSelections); + } + // sendInputes(newSelections, duration); // Send data to server + // return newSelections; + // }; + }; + + const handleSelectDuration = async (option: string) => { + if (await sendInputes(selections, option, widgetName)) { + setDuration(option); + } + // setDuration(option); + }; + + const handleNameChange = async (name:any) => { + console.log('name change requested',name); + + if (await sendInputes(selections, duration, name)) { + setWidgetName(name); + } + } + + return ( + <> +
+
+
Title
+ +
+ {[...Array(1)].map((_, index) => { + const inputKey = `input${index + 1}`; + return ( +
+
Input {index + 1}
+
+ handleSelect(inputKey, selectedData)} + onUnselect={() => handleSelect(inputKey, null)} + selectedValue={selections[inputKey]} // Load from Zustand + /> +
+ +
+
+
+ ); + })} +
+
+ {/*
+
Duration
+
+ +
+
*/} +
+ + ); +}; + +export default FleetEfficiencyInputComponent; \ No newline at end of file diff --git a/app/src/components/layout/sidebarRight/visualization/IotInputCards/InputSelecterComponent.tsx b/app/src/components/layout/sidebarRight/visualization/IotInputCards/InputSelecterComponent.tsx index 45ce802..04451d6 100644 --- a/app/src/components/layout/sidebarRight/visualization/IotInputCards/InputSelecterComponent.tsx +++ b/app/src/components/layout/sidebarRight/visualization/IotInputCards/InputSelecterComponent.tsx @@ -3,6 +3,9 @@ import LineGrapInput from './LineGrapInput' import BarChartInput from './BarChartInput' import PieChartInput from './PieChartInput' import FlotingWidgetInput from './FlotingWidgetInput' +import FleetEfficiencyInputComponent from './FleetEfficiencyInputComponent' +import Progress1Input from './Progress1Input' +import Progress2Input from './Progress2Input' import WarehouseThroughputInputComponent from './WarehouseThroughputInputComponent' import { useWidgetStore } from '../../../../../store/useWidgetStore' @@ -56,6 +59,24 @@ const InputSelecterComponent = () => { ) } + else if (selectedChartId && selectedChartId.type && selectedChartId.type === 'progress 1' ) { + return ( + <> +
2D Widget Input
+ + + ) + } + + else if (selectedChartId && selectedChartId.type && selectedChartId.type === 'progress 2' ) { + return ( + <> +
2D Widget Input
+ + + ) + } + else if (selectedChartId && selectedChartId.className && selectedChartId.className === 'warehouseThroughput floating' ) { return ( <> @@ -65,6 +86,24 @@ const InputSelecterComponent = () => { ) } + else if (selectedChartId && selectedChartId.className && selectedChartId.className === 'fleetEfficiency floating' ) { + return ( + <> +
Floting Widget Input
+ + + ) + } + + else if (selectedChartId && selectedChartId.className && selectedChartId.className === 'floating total-card' ) { + return ( + <> +
Floting Widget Input
+ + + ) + } + else { return (
No chart selected
diff --git a/app/src/components/layout/sidebarRight/visualization/IotInputCards/Progress1Input.tsx b/app/src/components/layout/sidebarRight/visualization/IotInputCards/Progress1Input.tsx new file mode 100644 index 0000000..545df45 --- /dev/null +++ b/app/src/components/layout/sidebarRight/visualization/IotInputCards/Progress1Input.tsx @@ -0,0 +1,171 @@ +import React, { useEffect, useState } from "react"; +import MultiLevelDropdown from "../../../../ui/inputs/MultiLevelDropDown"; +import { AddIcon } from "../../../../icons/ExportCommonIcons"; +import RegularDropDown from "../../../../ui/inputs/RegularDropDown"; +import useChartStore from "../../../../../store/useChartStore"; +import { useSelectedZoneStore } from "../../../../../store/useZoneStore"; +import { useWidgetStore } from "../../../../../store/useWidgetStore"; +import axios from "axios"; +import RenameInput from "../../../../ui/inputs/RenameInput"; + +type Props = {}; + +const Progress1Input = (props: Props) => { + const [widgetName, setWidgetName] = useState('Widget'); + const { setMeasurements, updateDuration, updateName } = useChartStore(); + const [duration, setDuration] = useState('1h') + const [dropDowndata, setDropDownData] = useState({}); + const [selections, setSelections] = useState>({}); + const { selectedZone } = useSelectedZoneStore(); + const { selectedChartId } = useWidgetStore(); + const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL; + const email = localStorage.getItem("email") || ""; + const organization = email?.split("@")[1]?.split(".")[0] + + useEffect(() => { + const fetchZoneData = async () => { + try { + const response = await axios.get(`http://${iotApiUrl}/getinput`); + if (response.status === 200) { + // console.log("dropdown data:", response.data); + setDropDownData(response.data); + } else { + console.log("Unexpected response:", response); + } + } catch (error) { + console.error("There was an error!", error); + } + }; + fetchZoneData(); + }, []); + + useEffect(() => { + const fetchSavedInputes = async () => { + if (selectedChartId.id !== "") { + try { + const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/WidgetData/${selectedChartId.id}/${organization}`); + if (response.status === 200) { + setSelections(response.data.Data.measurements) + setDuration(response.data.Data.duration) + setWidgetName(response.data.widgetName) + } else { + console.log("Unexpected response:", response); + } + } catch (error) { + console.error("There was an error!", error); + } + } + } + + fetchSavedInputes(); + + }, [selectedChartId.id]); + + // Sync Zustand state when component mounts + useEffect(() => { + setMeasurements(selections); + updateDuration(duration); + updateName(widgetName); + }, [selections, duration, widgetName]); + + + const sendInputes = async (inputMeasurement: any, inputDuration: any, inputName: any) => { + try { + const response = await axios.post(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/widget/save`, { + organization: organization, + zoneId: selectedZone.zoneId, + widget: { + id: selectedChartId.id, + panel: selectedChartId.panel, + widgetName: inputName, + Data: { + measurements: inputMeasurement, + duration: inputDuration + } + } + } as any); + if (response.status === 200) { + return true + } else { + console.log("Unexpected response:", response); + return false + } + } catch (error) { + console.error("There was an error!", error); + return false + } + } + + const handleSelect = async (inputKey: string, selectedData: { name: string; fields: string } | null) => { + + // async() => { + const newSelections = { ...selections }; + if (selectedData === null) { + delete newSelections[inputKey]; + } else { + newSelections[inputKey] = selectedData; + } + // setMeasurements(newSelections); // Update Zustand store + console.log(newSelections); + if (await sendInputes(newSelections, duration, widgetName)) { + setSelections(newSelections); + } + }; + + const handleSelectDuration = async (option: string) => { + if (await sendInputes(selections, option, widgetName)) { + setDuration(option); + } + }; + + const handleNameChange = async (name:any) => { + if (await sendInputes(selections, duration, name)) { + setWidgetName(name); + } + } + + return ( + <> +
+
+
Title
+ +
+ {[...Array(1)].map((_, index) => { + const inputKey = `input${index + 1}`; + return ( +
+
Input {index + 1}
+
+ handleSelect(inputKey, selectedData)} + onUnselect={() => handleSelect(inputKey, null)} + selectedValue={selections[inputKey]} // Load from Zustand + /> +
+ +
+
+
+ ); + })} +
+ {/*
+
+
Duration
+
+ +
+
+
*/} + + ); +}; + +export default Progress1Input; \ No newline at end of file diff --git a/app/src/components/layout/sidebarRight/visualization/IotInputCards/Progress2Input.tsx b/app/src/components/layout/sidebarRight/visualization/IotInputCards/Progress2Input.tsx new file mode 100644 index 0000000..3a11a29 --- /dev/null +++ b/app/src/components/layout/sidebarRight/visualization/IotInputCards/Progress2Input.tsx @@ -0,0 +1,171 @@ +import React, { useEffect, useState } from "react"; +import MultiLevelDropdown from "../../../../ui/inputs/MultiLevelDropDown"; +import { AddIcon } from "../../../../icons/ExportCommonIcons"; +import RegularDropDown from "../../../../ui/inputs/RegularDropDown"; +import useChartStore from "../../../../../store/useChartStore"; +import { useSelectedZoneStore } from "../../../../../store/useZoneStore"; +import { useWidgetStore } from "../../../../../store/useWidgetStore"; +import axios from "axios"; +import RenameInput from "../../../../ui/inputs/RenameInput"; + +type Props = {}; + +const Progress2Input = (props: Props) => { + const [widgetName, setWidgetName] = useState('Widget'); + const { setMeasurements, updateDuration, updateName } = useChartStore(); + const [duration, setDuration] = useState('1h') + const [dropDowndata, setDropDownData] = useState({}); + const [selections, setSelections] = useState>({}); + const { selectedZone } = useSelectedZoneStore(); + const { selectedChartId } = useWidgetStore(); + const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL; + const email = localStorage.getItem("email") || ""; + const organization = email?.split("@")[1]?.split(".")[0] + + useEffect(() => { + const fetchZoneData = async () => { + try { + const response = await axios.get(`http://${iotApiUrl}/getinput`); + if (response.status === 200) { + // console.log("dropdown data:", response.data); + setDropDownData(response.data); + } else { + console.log("Unexpected response:", response); + } + } catch (error) { + console.error("There was an error!", error); + } + }; + fetchZoneData(); + }, []); + + useEffect(() => { + const fetchSavedInputes = async () => { + if (selectedChartId.id !== "") { + try { + const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/WidgetData/${selectedChartId.id}/${organization}`); + if (response.status === 200) { + setSelections(response.data.Data.measurements) + setDuration(response.data.Data.duration) + setWidgetName(response.data.widgetName) + } else { + console.log("Unexpected response:", response); + } + } catch (error) { + console.error("There was an error!", error); + } + } + } + + fetchSavedInputes(); + + }, [selectedChartId.id]); + + // Sync Zustand state when component mounts + useEffect(() => { + setMeasurements(selections); + updateDuration(duration); + updateName(widgetName); + }, [selections, duration, widgetName]); + + + const sendInputes = async (inputMeasurement: any, inputDuration: any, inputName: any) => { + try { + const response = await axios.post(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/widget/save`, { + organization: organization, + zoneId: selectedZone.zoneId, + widget: { + id: selectedChartId.id, + panel: selectedChartId.panel, + widgetName: inputName, + Data: { + measurements: inputMeasurement, + duration: inputDuration + } + } + } as any); + if (response.status === 200) { + return true + } else { + console.log("Unexpected response:", response); + return false + } + } catch (error) { + console.error("There was an error!", error); + return false + } + } + + const handleSelect = async (inputKey: string, selectedData: { name: string; fields: string } | null) => { + + // async() => { + const newSelections = { ...selections }; + if (selectedData === null) { + delete newSelections[inputKey]; + } else { + newSelections[inputKey] = selectedData; + } + // setMeasurements(newSelections); // Update Zustand store + console.log(newSelections); + if (await sendInputes(newSelections, duration, widgetName)) { + setSelections(newSelections); + } + }; + + const handleSelectDuration = async (option: string) => { + if (await sendInputes(selections, option, widgetName)) { + setDuration(option); + } + }; + + const handleNameChange = async (name:any) => { + if (await sendInputes(selections, duration, name)) { + setWidgetName(name); + } + } + + return ( + <> +
+
+
Title
+ +
+ {[...Array(2)].map((_, index) => { + const inputKey = `input${index + 1}`; + return ( +
+
Input {index + 1}
+
+ handleSelect(inputKey, selectedData)} + onUnselect={() => handleSelect(inputKey, null)} + selectedValue={selections[inputKey]} // Load from Zustand + /> +
+ +
+
+
+ ); + })} +
+ {/*
+
+
Duration
+
+ +
+
+
*/} + + ); +}; + +export default Progress2Input; \ No newline at end of file diff --git a/app/src/components/ui/componets/DisplayZone.tsx b/app/src/components/ui/componets/DisplayZone.tsx index 3889139..9ce64cb 100644 --- a/app/src/components/ui/componets/DisplayZone.tsx +++ b/app/src/components/ui/componets/DisplayZone.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useRef, useState, useCallback } from "react"; -import { Widget } from "../../../store/useWidgetStore"; +import { useWidgetStore, Widget } from "../../../store/useWidgetStore"; import { MoveArrowLeft, MoveArrowRight } from "../../icons/SimulationIcons"; import { InfoIcon } from "../../icons/ExportCommonIcons"; import { useDroppedObjectsStore, useFloatingWidget } from "../../../store/useDroppedObjectsStore"; @@ -74,6 +74,7 @@ const DisplayZone: React.FC = ({ const [showLeftArrow, setShowLeftArrow] = useState(false); const [showRightArrow, setShowRightArrow] = useState(false); const { floatingWidget, setFloatingWidget } = useFloatingWidget() + const{setSelectedChartId}=useWidgetStore() // Function to calculate overflow state const updateOverflowState = useCallback(() => { @@ -152,9 +153,9 @@ const DisplayZone: React.FC = ({ async function handleSelect2dZoneData(zoneId: string, zoneName: string) { try { if (selectedZone?.zoneId === zoneId) { - return; } + setSelectedChartId(null) const email = localStorage.getItem("email") || ""; const organization = email?.split("@")[1]?.split(".")[0]; // Fetch data from backend diff --git a/app/src/components/ui/componets/DraggableWidget.tsx b/app/src/components/ui/componets/DraggableWidget.tsx index db8c408..11b4bfb 100644 --- a/app/src/components/ui/componets/DraggableWidget.tsx +++ b/app/src/components/ui/componets/DraggableWidget.tsx @@ -5,6 +5,8 @@ import BarGraphComponent from "../realTimeVis/charts/BarGraphComponent"; import LineGraphComponent from "../realTimeVis/charts/LineGraphComponent"; import DoughnutGraphComponent from "../realTimeVis/charts/DoughnutGraphComponent"; import PolarAreaGraphComponent from "../realTimeVis/charts/PolarAreaGraphComponent"; +import ProgressCard1 from "../realTimeVis/charts/ProgressCard1"; +import ProgressCard2 from "../realTimeVis/charts/ProgressCard2"; import { DeleteIcon, DublicateIcon, @@ -203,6 +205,7 @@ export const DraggableWidget = ({ onReorder(fromIndex, toIndex); // Call the reorder function passed as a prop } }; + console.log("widget.type", widget.type); return ( <> @@ -249,9 +252,12 @@ export const DraggableWidget = ({ )} {/* Render charts based on widget type */} - - {widget.type === "progress" && ( - + + {widget.type === "progress 1" && ( + + )} + {widget.type === "progress 2" && ( + )} {widget.type === "line" && ( )} {widget.type === "bar" && ( @@ -277,14 +275,6 @@ export const DraggableWidget = ({ title={widget.title} fontSize={widget.fontSize} fontWeight={widget.fontWeight} - // data={{ - // measurements: [ - // { name: "testDevice", fields: "powerConsumption" }, - // { name: "furnace", fields: "powerConsumption" }, - // ], - // interval: 1000, - // duration: "1h", - // }} /> )} {widget.type === "pie" && ( @@ -294,14 +284,6 @@ export const DraggableWidget = ({ title={widget.title} fontSize={widget.fontSize} fontWeight={widget.fontWeight} - // data={{ - // measurements: [ - // { name: "testDevice", fields: "powerConsumption" }, - // { name: "furnace", fields: "powerConsumption" }, - // ], - // interval: 1000, - // duration: "1h", - // }} /> )} {widget.type === "doughnut" && ( diff --git a/app/src/components/ui/componets/Dropped3dWidget.tsx b/app/src/components/ui/componets/Dropped3dWidget.tsx index 94c5e45..0a3534a 100644 --- a/app/src/components/ui/componets/Dropped3dWidget.tsx +++ b/app/src/components/ui/componets/Dropped3dWidget.tsx @@ -126,6 +126,7 @@ export default function Dropped3dWidgets() { return ( <> {activeZoneWidgets.map(({ id, type, position }) => { + console.log('Typeeeeeeeeeeee',type); switch (type) { case "ui-Widget 1": return ; diff --git a/app/src/components/ui/realTimeVis/charts/ProgressCard1.tsx b/app/src/components/ui/realTimeVis/charts/ProgressCard1.tsx new file mode 100644 index 0000000..8fe8777 --- /dev/null +++ b/app/src/components/ui/realTimeVis/charts/ProgressCard1.tsx @@ -0,0 +1,105 @@ +import { StockIncreseIcon } from "../../../icons/RealTimeVisulationIcons"; +import React, { useEffect, useMemo, useState } from "react"; +import { Line } from "react-chartjs-2"; +import io from "socket.io-client"; +import useChartStore from "../../../../store/useChartStore"; +import { useWidgetStore } from "../../../../store/useWidgetStore"; +import axios from "axios"; + +const ProgressCard1 = ({ id,title,}: { + id: string, + title: string; +}) => { + const { measurements: chartMeasurements, duration: chartDuration, name: widgetName } = useChartStore(); + const [measurements, setmeasurements] = useState({}); + const [duration, setDuration] = useState("1h") + const [name, setName] = useState(title) + const [value, setValue] = useState('') + const { selectedChartId } = useWidgetStore(); + + const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL; + const email = localStorage.getItem("email") || ""; + const organization = email?.split("@")[1]?.split(".")[0] + + + useEffect(() => { + const socket = io(`http://${iotApiUrl}`); + if (!iotApiUrl || !measurements || Object.keys(measurements).length === 0) return; + + const inputData = { + measurements, + duration, + interval: 1000, + }; + + + const startStream = () => { + socket.emit("lastInput", inputData); + }; + + socket.on("connect", startStream); + + socket.on("lastOutput", (response) => { + const responseData = response.input1; + setValue(responseData); + + }); + + return () => { + socket.off("lastOutput"); + socket.emit("stop_stream"); // Stop streaming when component unmounts + socket.disconnect(); + }; + }, [measurements, duration]); + + const fetchSavedInputes = async() => { + + if (id !== "") { + try { + const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/WidgetData/${id}/${organization}`); + if (response.status === 200) { + setmeasurements(response.data.Data.measurements) + setDuration(response.data.Data.duration) + setName(response.data.widgetName) + } else { + console.log("Unexpected response:", response); + } + } catch (error) { + console.error("There was an error!", error); + } + } + } + + useEffect(() => { + fetchSavedInputes(); + }, []); + + useEffect(() => { + if (selectedChartId?.id === id) { + fetchSavedInputes(); + } + } + ,[chartMeasurements, chartDuration, widgetName]) + + return( +
+
{name}
+
+ + +
{value}
+
Units
+ +
+
{ + measurements ? `${measurements?.input1?.fields}` : 'description'}
+
+
+ +
+
+
+ ) +}; + +export default ProgressCard1; \ No newline at end of file diff --git a/app/src/components/ui/realTimeVis/charts/ProgressCard2.tsx b/app/src/components/ui/realTimeVis/charts/ProgressCard2.tsx new file mode 100644 index 0000000..083bbed --- /dev/null +++ b/app/src/components/ui/realTimeVis/charts/ProgressCard2.tsx @@ -0,0 +1,125 @@ +import { StockIncreseIcon } from "../../../icons/RealTimeVisulationIcons"; +import React, { useEffect, useMemo, useState } from "react"; +import { Line } from "react-chartjs-2"; +import io from "socket.io-client"; +import useChartStore from "../../../../store/useChartStore"; +import { useWidgetStore } from "../../../../store/useWidgetStore"; +import axios from "axios"; + +const ProgressCard2 = ({ id,title,}: { + id: string, + title: string; +}) => { + const { measurements: chartMeasurements, duration: chartDuration, name: widgetName } = useChartStore(); + const [measurements, setmeasurements] = useState({}); + const [duration, setDuration] = useState("1h") + const [name, setName] = useState(title) + const [value1, setValue1] = useState('') + const [value2, setValue2] = useState('') + const { selectedChartId } = useWidgetStore(); + + const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL; + const email = localStorage.getItem("email") || ""; + const organization = email?.split("@")[1]?.split(".")[0] + + + useEffect(() => { + if (!iotApiUrl || !measurements || Object.keys(measurements).length === 0) return; + + const socket = io(`http://${iotApiUrl}`); + + const inputData = { + measurements, + duration, + interval: 1000, + }; + + + const startStream = () => { + socket.emit("lastInput", inputData); + }; + + socket.on("connect", startStream); + + socket.on("lastOutput", (response) => { + const responseData1 = response.input1; + const responseData2 = response.input2; + setValue1(responseData1); + setValue2(responseData2); + }); + + return () => { + socket.off("lastOutput"); + socket.emit("stop_stream"); // Stop streaming when component unmounts + socket.disconnect(); + }; + }, [measurements, duration]); + + const fetchSavedInputes = async() => { + + if (id !== "") { + try { + const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/WidgetData/${id}/${organization}`); + if (response.status === 200) { + setmeasurements(response.data.Data.measurements) + setDuration(response.data.Data.duration) + setName(response.data.widgetName) + } else { + console.log("Unexpected response:", response); + } + } catch (error) { + console.error("There was an error!", error); + } + } + } + + useEffect(() => { + fetchSavedInputes(); + }, []); + + useEffect(() => { + if (selectedChartId?.id === id) { + fetchSavedInputes(); + } + } + ,[chartMeasurements, chartDuration, widgetName]) + + return( +
+
{name}
+ +
+ + +
{value1}
+
Units
+ +
+
{ + measurements ? `${measurements?.input1?.fields}` : 'description'}
+
+
+ +
+
+ +
+ + +
{value2}
+
Units
+ +
+
{ + measurements ? `${measurements?.input2?.fields}` : 'description'}
+
+
+ +
+
+ +
+ ) +}; + +export default ProgressCard2; \ No newline at end of file diff --git a/app/src/components/ui/realTimeVis/floating/FleetEfficiencyComponent.tsx b/app/src/components/ui/realTimeVis/floating/FleetEfficiencyComponent.tsx index e9221e8..2cf12e0 100644 --- a/app/src/components/ui/realTimeVis/floating/FleetEfficiencyComponent.tsx +++ b/app/src/components/ui/realTimeVis/floating/FleetEfficiencyComponent.tsx @@ -1,14 +1,93 @@ -import React,{ useEffect, useState} from 'react' +import React, { useState, useEffect } from 'react' +import { Line } from 'react-chartjs-2' +import useChartStore from '../../../../store/useChartStore'; +import { useWidgetStore } from '../../../../store/useWidgetStore'; +import axios from 'axios'; +import io from "socket.io-client"; const FleetEfficiencyComponent = ({object}: any) => { const [ progress, setProgress ] = useState(0) + const [measurements, setmeasurements] = useState({}); + const [duration, setDuration] = useState("1h") + const [name, setName] = useState(object.header ? object.header : '') + const email = localStorage.getItem("email") || ""; + const organization = email?.split("@")[1]?.split(".")[0] + const { header, flotingDuration, flotingMeasurements } = useChartStore(); + const { selectedChartId } = useWidgetStore(); + + const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL; // Calculate the rotation angle for the progress bar const rotationAngle = 45 + progress * 1.8; + + + useEffect(() => { + if (!iotApiUrl || !measurements || Object.keys(measurements).length === 0) return; + + const socket = io(`http://${iotApiUrl}`); + + const inputData = { + measurements, + duration, + interval: 1000, + }; + + + const startStream = () => { + socket.emit("lastInput", inputData); + }; + + socket.on("connect", startStream); + + socket.on("lastOutput", (response) => { + const responseData = response.input1; + console.log(responseData); + + if (typeof responseData === "number") { + console.log("It's a number!"); + setProgress(responseData); + } + }); + + return () => { + socket.off("lastOutput"); + socket.emit("stop_stream"); // Stop streaming when component unmounts + socket.disconnect(); + }; + }, [measurements, duration, iotApiUrl]); + + const fetchSavedInputes = async() => { + + if (object?.id !== "") { + try { + const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/A_floatWidget/${object?.id}/${organization}`); + if (response.status === 200) { + setmeasurements(response.data.Data.measurements) + setDuration(response.data.Data.duration) + setName(response.data.header) + } else { + console.log("Unexpected response:", response); + } + } catch (error) { + console.error("There was an error!", error); + } + } + } + + useEffect(() => { + fetchSavedInputes(); + }, []); + + useEffect(() => { + if (selectedChartId?.id === object?.id) { + fetchSavedInputes(); + } + } + ,[header, flotingDuration, flotingMeasurements]) return ( <> -

Fleet Efficiency

+

{name}

diff --git a/app/src/components/ui/realTimeVis/floating/TotalCardComponent.tsx b/app/src/components/ui/realTimeVis/floating/TotalCardComponent.tsx index 00ec479..da03fce 100644 --- a/app/src/components/ui/realTimeVis/floating/TotalCardComponent.tsx +++ b/app/src/components/ui/realTimeVis/floating/TotalCardComponent.tsx @@ -1,21 +1,95 @@ -import React from 'react' -import { WalletIcon } from '../../../icons/3dChartIcons' +import React, { useState, useEffect } from 'react' +import { Line } from 'react-chartjs-2' +import useChartStore from '../../../../store/useChartStore'; import { useWidgetStore } from '../../../../store/useWidgetStore'; +import axios from 'axios'; +import io from "socket.io-client"; +import { WalletIcon } from '../../../icons/3dChartIcons'; + const TotalCardComponent = ({ object }: any) => { - const { setSelectedChartId } = - useWidgetStore(); + const [ progress, setProgress ] = useState(0) + const [measurements, setmeasurements] = useState({}); + const [duration, setDuration] = useState("1h") + const [name, setName] = useState(object.header ? object.header : '') + const email = localStorage.getItem("email") || ""; + const organization = email?.split("@")[1]?.split(".")[0] + const { header, flotingDuration, flotingMeasurements } = useChartStore(); + const { selectedChartId } = useWidgetStore(); + + const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL; + + useEffect(() => { + if (!iotApiUrl || !measurements || Object.keys(measurements).length === 0) return; + + const socket = io(`http://${iotApiUrl}`); + + const inputData = { + measurements, + duration, + interval: 1000, + }; + + + const startStream = () => { + socket.emit("lastInput", inputData); + }; + + socket.on("connect", startStream); + + socket.on("lastOutput", (response) => { + const responseData = response.input1; + + if (typeof responseData === "number") { + setProgress(responseData); + } + }); + + return () => { + socket.off("lastOutput"); + socket.emit("stop_stream"); // Stop streaming when component unmounts + socket.disconnect(); + }; + }, [measurements, duration, iotApiUrl]); + + const fetchSavedInputes = async() => { + + if (object?.id !== "") { + try { + const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/A_floatWidget/${object?.id}/${organization}`); + if (response.status === 200) { + setmeasurements(response.data.Data.measurements) + setDuration(response.data.Data.duration) + setName(response.data.header) + } else { + console.log("Unexpected response:", response); + } + } catch (error) { + console.error("There was an error!", error); + } + } + } + + useEffect(() => { + fetchSavedInputes(); + }, []); + + useEffect(() => { + if (selectedChartId?.id === object?.id) { + fetchSavedInputes(); + } + } + ,[header, flotingDuration, flotingMeasurements]) + return ( <> -
{ - setSelectedChartId(object.id) - }}> -
{object.header}
+
+
{name}
-
{object.value}
+
{progress}
{object.per}
diff --git a/app/src/styles/pages/realTimeViz.scss b/app/src/styles/pages/realTimeViz.scss index 4440e6d..17407cb 100644 --- a/app/src/styles/pages/realTimeViz.scss +++ b/app/src/styles/pages/realTimeViz.scss @@ -546,9 +546,9 @@ .floating-wrapper { .icon { - width: 25px !important; - height: 25px !important; - background-color: transparent; + // width: 25px !important; + // height: 25px !important; + // background-color: transparent; } .kebab { @@ -560,6 +560,7 @@ z-index: 10; cursor: pointer; @include flex-center; + background-color: transparent !important; } .kebab-options { From 5666416cb9bc2f3b85b2c2c0e1038e3653ceb256 Mon Sep 17 00:00:00 2001 From: Vishnu Date: Wed, 2 Apr 2025 10:38:14 +0530 Subject: [PATCH 10/12] fix: ensure theme is set on initialization --- app/src/utils/theme.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/utils/theme.ts b/app/src/utils/theme.ts index 1fce2eb..7bef114 100644 --- a/app/src/utils/theme.ts +++ b/app/src/utils/theme.ts @@ -17,3 +17,5 @@ export function toggleTheme() { document.documentElement.setAttribute('data-theme', newTheme); localStorage.setItem('theme', newTheme); } + +setTheme(); \ No newline at end of file From f0f7064b0ae87754b0276141651787d40ac7be5f Mon Sep 17 00:00:00 2001 From: gabriel Date: Wed, 2 Apr 2025 18:06:51 +0530 Subject: [PATCH 11/12] added iot data and input card for 3D widget --- .../3D-cards/cards/ProductionCapacity.tsx | 113 ++++++++++- .../3D-cards/cards/ReturnOfInvestment.tsx | 110 ++++++++++- .../layout/3D-cards/cards/StateWorking.tsx | 133 +++++++++++-- .../layout/3D-cards/cards/Throughput.tsx | 109 ++++++++++- .../IotInputCards/InputSelecterComponent.tsx | 39 ++++ .../IotInputCards/Widget2InputCard3D.tsx | 165 ++++++++++++++++ .../IotInputCards/Widget3InputCard3D.tsx | 158 ++++++++++++++++ .../IotInputCards/Widget4InputCard3D.tsx | 178 ++++++++++++++++++ .../ui/componets/Dropped3dWidget.tsx | 8 +- 9 files changed, 982 insertions(+), 31 deletions(-) create mode 100644 app/src/components/layout/sidebarRight/visualization/IotInputCards/Widget2InputCard3D.tsx create mode 100644 app/src/components/layout/sidebarRight/visualization/IotInputCards/Widget3InputCard3D.tsx create mode 100644 app/src/components/layout/sidebarRight/visualization/IotInputCards/Widget4InputCard3D.tsx diff --git a/app/src/components/layout/3D-cards/cards/ProductionCapacity.tsx b/app/src/components/layout/3D-cards/cards/ProductionCapacity.tsx index 4da32bc..05c2814 100644 --- a/app/src/components/layout/3D-cards/cards/ProductionCapacity.tsx +++ b/app/src/components/layout/3D-cards/cards/ProductionCapacity.tsx @@ -1,5 +1,5 @@ import { Html } from "@react-three/drei"; -import React from "react"; +import React, { useEffect, useMemo, useState } from "react"; import { Bar } from "react-chartjs-2"; import { Chart as ChartJS, @@ -11,6 +11,11 @@ import { Legend, TooltipItem, // Import TooltipItem for typing } from "chart.js"; +import { ThroughputIcon } from "../../../icons/3dChartIcons"; +import { useWidgetStore } from "../../../../store/useWidgetStore"; +import useChartStore from "../../../../store/useChartStore"; +import axios from "axios"; +import io from "socket.io-client"; // Register ChartJS components ChartJS.register( @@ -22,12 +27,28 @@ ChartJS.register( Legend ); interface ProductionCapacityProps { + id: string; + type: string; position: [number, number, number]; } -const ProductionCapacity : React.FC = ({ position }) => { +const ProductionCapacity : React.FC = ({ id, type, position }) => { + + const { selectedChartId,setSelectedChartId } = useWidgetStore(); + + const { measurements: chartMeasurements, duration: chartDuration, name: widgetName } = useChartStore(); + const [measurements, setmeasurements] = useState({}); + const [duration, setDuration] = useState("1h") + const [name, setName] = useState("Widget") + const [chartData, setChartData] = useState<{ labels: string[]; datasets: any[] }>({ + labels: [], + datasets: [], + }); + const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL; + const email = localStorage.getItem("email") || ""; + const organization = email?.split("@")[1]?.split(".")[0] // Chart data for a week - const chartData = { + const defaultChartData = { labels: ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"], // Days of the week datasets: [ { @@ -78,12 +99,94 @@ const ProductionCapacity : React.FC = ({ position }) => }, }; + useEffect(() => { + if (!iotApiUrl || !measurements || Object.keys(measurements).length === 0) return; + + const socket = io(`http://${iotApiUrl}`); + + const inputData = { + measurements, + duration, + interval: 1000, + }; + + + const startStream = () => { + socket.emit("lineInput", inputData); + }; + + socket.on("connect", startStream); + + socket.on("lineOutput", (response) => { + const responseData = response.data; + + // Extract timestamps and values + const labels = responseData.time; + const datasets = Object.keys(measurements).map((key) => { + const measurement = measurements[key]; + const datasetKey = `${measurement.name}.${measurement.fields}`; + return { + label: datasetKey, + data: responseData[datasetKey]?.values ?? [], + backgroundColor: "#6f42c1", // Theme color + borderColor: "#6f42c1", + borderWidth: 1, + borderRadius: 8, // Rounded corners for the bars + borderSkipped: false, // Ensure all corners are rounded + }; + }); + + setChartData({ labels, datasets }); + }); + + return () => { + socket.off("lineOutput"); + socket.emit("stop_stream"); // Stop streaming when component unmounts + socket.disconnect(); + }; + }, [measurements, duration, iotApiUrl]); + + const fetchSavedInputes = async() => { + + if (id !== "") { + try { + const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/widget3D/${id}/${organization}`); + if (response.status === 200) { + setmeasurements(response.data.Data.measurements) + setDuration(response.data.Data.duration) + setName(response.data.widgetName) + } else { + console.log("Unexpected response:", response); + } + } catch (error) { + console.error("There was an error!", error); + } + } + } + + useEffect(() => { + fetchSavedInputes(); + }, []); + + useEffect(() => { + if (selectedChartId?.id === id) { + fetchSavedInputes(); + } + } + ,[chartMeasurements, chartDuration, widgetName]) + return ( -
+
setSelectedChartId({ + id: id, + type: type + }) + }>
Production Capacity
@@ -104,7 +207,7 @@ const ProductionCapacity : React.FC = ({ position }) =>
{" "}
{/* Bar Chart */} - + 0 ? chartData : defaultChartData } options={chartOptions} />
diff --git a/app/src/components/layout/3D-cards/cards/ReturnOfInvestment.tsx b/app/src/components/layout/3D-cards/cards/ReturnOfInvestment.tsx index 8e8c444..5750bfd 100644 --- a/app/src/components/layout/3D-cards/cards/ReturnOfInvestment.tsx +++ b/app/src/components/layout/3D-cards/cards/ReturnOfInvestment.tsx @@ -1,5 +1,5 @@ import { Html } from "@react-three/drei"; -import React from "react"; +import React, { useEffect, useMemo, useState } from "react"; import { Line } from "react-chartjs-2"; import { Chart as ChartJS, @@ -12,6 +12,10 @@ import { ChartOptions, } from "chart.js"; import { WavyIcon } from "../../../icons/3dChartIcons"; +import { useWidgetStore } from "../../../../store/useWidgetStore"; +import useChartStore from "../../../../store/useChartStore"; +import axios from "axios"; +import io from "socket.io-client"; // Register Chart.js components ChartJS.register( @@ -36,9 +40,24 @@ const SmoothLineGraphComponent: React.FC = ({ return ; }; interface ReturnOfInvestmentProps { + id: string; + type: string; position: [number, number, number]; } -const ReturnOfInvestment: React.FC = ({ position }) => { +const ReturnOfInvestment: React.FC = ({ id, type, position }) => { + + const { selectedChartId,setSelectedChartId } = useWidgetStore(); + const { measurements: chartMeasurements, duration: chartDuration, name: widgetName } = useChartStore(); + const [measurements, setmeasurements] = useState({}); + const [duration, setDuration] = useState("1h") + const [name, setName] = useState("Widget") + const [chartData, setChartData] = useState<{ labels: string[]; datasets: any[] }>({ + labels: [], + datasets: [], + }); + const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL; + const email = localStorage.getItem("email") || ""; + const organization = email?.split("@")[1]?.split(".")[0] // Improved sample data for the smooth curve graph (single day) const graphData: ChartData<"line"> = { labels: [ @@ -107,16 +126,99 @@ const ReturnOfInvestment: React.FC = ({ position }) => }, }; + useEffect(() => { + if (!iotApiUrl || !measurements || Object.keys(measurements).length === 0) return; + + const socket = io(`http://${iotApiUrl}`); + + const inputData = { + measurements, + duration, + interval: 1000, + }; + + + const startStream = () => { + socket.emit("lineInput", inputData); + }; + + socket.on("connect", startStream); + + socket.on("lineOutput", (response) => { + const responseData = response.data; + + // Extract timestamps and values + const labels = responseData.time; + const datasets = Object.keys(measurements).map((key, index) => { + const measurement = measurements[key]; + const datasetKey = `${measurement.name}.${measurement.fields}`; + return { + label: datasetKey, + data: responseData[datasetKey]?.values ?? [], + borderColor: index === 0 ? "rgba(75, 192, 192, 1)": "rgba(255, 99, 132, 1)", // Light blue color + backgroundColor: index === 0 ? "rgba(75, 192, 192, 0.2)": "rgba(255, 99, 132, 0.2)", + fill: true, + tension: 0.4, // Smooth curve effect + pointRadius: 0, // Hide dots + pointHoverRadius: 0, // Hide hover dots + }; + }); + + setChartData({ labels, datasets }); + }); + + return () => { + socket.off("lineOutput"); + socket.emit("stop_stream"); // Stop streaming when component unmounts + socket.disconnect(); + }; + }, [measurements, duration, iotApiUrl]); + + const fetchSavedInputes = async() => { + + if (id !== "") { + try { + const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/widget3D/${id}/${organization}`); + if (response.status === 200) { + setmeasurements(response.data.Data.measurements) + setDuration(response.data.Data.duration) + setName(response.data.widgetName) + } else { + console.log("Unexpected response:", response); + } + } catch (error) { + console.error("There was an error!", error); + } + } + } + + useEffect(() => { + fetchSavedInputes(); + }, []); + + useEffect(() => { + if (selectedChartId?.id === id) { + fetchSavedInputes(); + } + } + ,[chartMeasurements, chartDuration, widgetName]) + return ( -
+
setSelectedChartId({ + id: id, + type: type + }) + }>
Return of Investment
{/* Smooth curve graph with two datasets */} - + 0 ? chartData : graphData} options={graphOptions} />
diff --git a/app/src/components/layout/3D-cards/cards/StateWorking.tsx b/app/src/components/layout/3D-cards/cards/StateWorking.tsx index 25ee39b..d293406 100644 --- a/app/src/components/layout/3D-cards/cards/StateWorking.tsx +++ b/app/src/components/layout/3D-cards/cards/StateWorking.tsx @@ -1,28 +1,111 @@ import { Html } from "@react-three/drei"; +import React, { useEffect, useMemo, useState } from "react"; +import { useWidgetStore } from "../../../../store/useWidgetStore"; +import useChartStore from "../../../../store/useChartStore"; +import axios from "axios"; +import io from "socket.io-client"; + // import image from "../../../../assets/image/temp/image.png"; interface StateWorkingProps { + id:string; + type: string; position: [number, number, number]; } -const StateWorking: React.FC = ({ position }) => { - const datas = [ - { key: "Oil Tank:", value: "24/341" }, - { key: "Oil Refin:", value: 36.023 }, - { key: "Transmission:", value: 36.023 }, - { key: "Fuel:", value: 36732 }, - { key: "Power:", value: 1300 }, - { key: "Time:", value: 13 - 9 - 2023 }, - ]; +const StateWorking: React.FC = ({ id, type, position }) => { + const { selectedChartId, setSelectedChartId } = useWidgetStore(); + const { measurements: chartMeasurements, duration: chartDuration, name: widgetName } = useChartStore(); + const [measurements, setmeasurements] = useState({}); + const [duration, setDuration] = useState("1h") + const [name, setName] = useState("Widget") + const [datas, setDatas] = useState({}); + const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL; + const email = localStorage.getItem("email") || ""; + const organization = email?.split("@")[1]?.split(".")[0] + // const datas = [ + // { key: "Oil Tank:", value: "24/341" }, + // { key: "Oil Refin:", value: 36.023 }, + // { key: "Transmission:", value: 36.023 }, + // { key: "Fuel:", value: 36732 }, + // { key: "Power:", value: 1300 }, + // { key: "Time:", value: 13 - 9 - 2023 }, + // ]; + + useEffect(() => { + if (!iotApiUrl || !measurements || Object.keys(measurements).length === 0) return; + const socket = io(`http://${iotApiUrl}`); + const inputData = { + measurements, + duration, + interval: 1000, + }; + const startStream = () => { + socket.emit("lastInput", inputData); + }; + socket.on("connect", startStream); + socket.on("lastOutput", (response) => { + const responseData = response; + console.log("responceeeeeeeeeee",response); + + setDatas(responseData); + }); + + return () => { + socket.off("lastOutput"); + socket.emit("stop_stream"); // Stop streaming when component unmounts + socket.disconnect(); + }; + }, [measurements, duration, iotApiUrl]); + + const fetchSavedInputes = async() => { + + if (id !== "") { + try { + const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/widget3D/${id}/${organization}`); + if (response.status === 200) { + setmeasurements(response.data.Data.measurements) + setDuration(response.data.Data.duration) + setName(response.data.widgetName) + } else { + console.log("Unexpected response:", response); + } + } catch (error) { + console.error("There was an error!", error); + } + } + } + + console.log("dataaaaa",datas); + + + useEffect(() => { + fetchSavedInputes(); + }, []); + + useEffect(() => { + if (selectedChartId?.id === id) { + fetchSavedInputes(); + } + } + ,[chartMeasurements, chartDuration, widgetName]) + + return ( -
+
setSelectedChartId({ + id: id, + type: type + }) + }>
State - Working . + {datas?.input1 ? datas.input1 : 'input1'} .
@@ -31,12 +114,36 @@ const StateWorking: React.FC = ({ position }) => {
{/* Data */}
- {datas.map((data, index) => ( + {/* {datas.map((data, index) => (
{data.key}
{data.value}
- ))} + ))} */} +
+
{measurements?.input2?.fields ? measurements.input2.fields : 'input2'}
+
{datas?.input2 ? datas.input2 : 'data'}
+
+
+
{measurements?.input3?.fields ? measurements.input3.fields : 'input3'}
+
{datas?.input3 ? datas.input3 : 'data'}
+
+
+
{measurements?.input4?.fields ? measurements.input4.fields : 'input4'}
+
{datas?.input4 ? datas.input4 : 'data'}
+
+
+
{measurements?.input5?.fields ? measurements.input5.fields : 'input5'}
+
{datas?.input5 ? datas.input5 : 'data'}
+
+
+
{measurements?.input6?.fields ? measurements.input6.fields : 'input6'}
+
{datas?.input6 ? datas.input6 : 'data'}
+
+
+
{measurements?.input7?.fields ? measurements.input7.fields : 'input7'}
+
{datas?.input7 ? datas.input7 : 'data'}
+
diff --git a/app/src/components/layout/3D-cards/cards/Throughput.tsx b/app/src/components/layout/3D-cards/cards/Throughput.tsx index 220c2a2..700914c 100644 --- a/app/src/components/layout/3D-cards/cards/Throughput.tsx +++ b/app/src/components/layout/3D-cards/cards/Throughput.tsx @@ -1,5 +1,5 @@ import { Html } from "@react-three/drei"; -import React from "react"; +import React, { useEffect, useMemo, useState } from "react"; import { Line } from "react-chartjs-2"; import { Chart as ChartJS, @@ -14,6 +14,10 @@ import { ChartOptions, } from "chart.js"; import { ThroughputIcon } from "../../../icons/3dChartIcons"; +import { useWidgetStore } from "../../../../store/useWidgetStore"; +import useChartStore from "../../../../store/useChartStore"; +import axios from "axios"; +import io from "socket.io-client"; // Register Chart.js components ChartJS.register( @@ -38,10 +42,25 @@ const LineGraphComponent: React.FC = ({ data, options }) => { }; interface ThroughputProps { + id: string; + type: string; position: [number, number, number]; } -const Throughput: React.FC = ({ position }) => { +const Throughput: React.FC = ({ id, type, position }) => { + + const { selectedChartId,setSelectedChartId } = useWidgetStore(); + const { measurements: chartMeasurements, duration: chartDuration, name: widgetName } = useChartStore(); + const [measurements, setmeasurements] = useState({}); + const [duration, setDuration] = useState("1h") + const [name, setName] = useState("Widget") + const [chartData, setChartData] = useState<{ labels: string[]; datasets: any[] }>({ + labels: [], + datasets: [], + }); + const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL; + const email = localStorage.getItem("email") || ""; + const organization = email?.split("@")[1]?.split(".")[0] // Sample data for the line graph const graphData: ChartData<"line"> = { @@ -90,13 +109,93 @@ const Throughput: React.FC = ({ position }) => { }, }; + useEffect(() => { + if (!iotApiUrl || !measurements || Object.keys(measurements).length === 0) return; + + const socket = io(`http://${iotApiUrl}`); + + const inputData = { + measurements, + duration, + interval: 1000, + }; + + + const startStream = () => { + socket.emit("lineInput", inputData); + }; + + socket.on("connect", startStream); + + socket.on("lineOutput", (response) => { + const responseData = response.data; + + // Extract timestamps and values + const labels = responseData.time; + const datasets = Object.keys(measurements).map((key) => { + const measurement = measurements[key]; + const datasetKey = `${measurement.name}.${measurement.fields}`; + return { + label: datasetKey, + data: responseData[datasetKey]?.values ?? [], + borderColor: "rgba(75, 192, 192, 1)", + backgroundColor: "rgba(75, 192, 192, 0.2)", + fill: true, + }; + }); + + setChartData({ labels, datasets }); + }); + + return () => { + socket.off("lineOutput"); + socket.emit("stop_stream"); // Stop streaming when component unmounts + socket.disconnect(); + }; + }, [measurements, duration, iotApiUrl]); + + const fetchSavedInputes = async() => { + + if (id !== "") { + try { + const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/widget3D/${id}/${organization}`); + if (response.status === 200) { + setmeasurements(response.data.Data.measurements) + setDuration(response.data.Data.duration) + setName(response.data.widgetName) + } else { + console.log("Unexpected response:", response); + } + } catch (error) { + console.error("There was an error!", error); + } + } + } + + useEffect(() => { + fetchSavedInputes(); + }, []); + + useEffect(() => { + if (selectedChartId?.id === id) { + fetchSavedInputes(); + } + } + ,[chartMeasurements, chartDuration, widgetName]) + return ( -
-
Throughput
+
setSelectedChartId({ + id: id, + type: type + }) + }> +
{name}
@@ -119,7 +218,7 @@ const Throughput: React.FC = ({ position }) => {
{/* Line graph using react-chartjs-2 */} - + 0 ? chartData : graphData} options={graphOptions} />
You made an extra $1256.13 this month diff --git a/app/src/components/layout/sidebarRight/visualization/IotInputCards/InputSelecterComponent.tsx b/app/src/components/layout/sidebarRight/visualization/IotInputCards/InputSelecterComponent.tsx index 04451d6..3b44fc2 100644 --- a/app/src/components/layout/sidebarRight/visualization/IotInputCards/InputSelecterComponent.tsx +++ b/app/src/components/layout/sidebarRight/visualization/IotInputCards/InputSelecterComponent.tsx @@ -6,6 +6,9 @@ import FlotingWidgetInput from './FlotingWidgetInput' import FleetEfficiencyInputComponent from './FleetEfficiencyInputComponent' import Progress1Input from './Progress1Input' import Progress2Input from './Progress2Input' +import Widget2InputCard3D from './Widget2InputCard3D' +import Widget3InputCard3D from './Widget3InputCard3D' +import Widget4InputCard3D from './Widget4InputCard3D' import WarehouseThroughputInputComponent from './WarehouseThroughputInputComponent' import { useWidgetStore } from '../../../../../store/useWidgetStore' @@ -104,6 +107,42 @@ const InputSelecterComponent = () => { ) } + else if (selectedChartId && selectedChartId.type && selectedChartId.type === 'ui-Widget 1' ) { + return ( + <> +
3D Widget Input
+ + + ) + } + + else if (selectedChartId && selectedChartId.type && selectedChartId.type === 'ui-Widget 2' ) { + return ( + <> +
3D Widget Input
+ + + ) + } + + else if (selectedChartId && selectedChartId.type && selectedChartId.type === 'ui-Widget 3' ) { + return ( + <> +
3D Widget Input
+ + + ) + } + + else if (selectedChartId && selectedChartId.type && selectedChartId.type === 'ui-Widget 4' ) { + return ( + <> +
3D Widget Input
+ + + ) + } + else { return (
No chart selected
diff --git a/app/src/components/layout/sidebarRight/visualization/IotInputCards/Widget2InputCard3D.tsx b/app/src/components/layout/sidebarRight/visualization/IotInputCards/Widget2InputCard3D.tsx new file mode 100644 index 0000000..bad0afc --- /dev/null +++ b/app/src/components/layout/sidebarRight/visualization/IotInputCards/Widget2InputCard3D.tsx @@ -0,0 +1,165 @@ +import React, { useEffect, useState } from "react"; +import MultiLevelDropdown from "../../../../ui/inputs/MultiLevelDropDown"; +import { AddIcon } from "../../../../icons/ExportCommonIcons"; +import RegularDropDown from "../../../../ui/inputs/RegularDropDown"; +import useChartStore from "../../../../../store/useChartStore"; +import { useSelectedZoneStore } from "../../../../../store/useZoneStore"; +import { useWidgetStore } from "../../../../../store/useWidgetStore"; +import axios from "axios"; +import RenameInput from "../../../../ui/inputs/RenameInput"; + +type Props = {}; + +const Widget2InputCard3D = (props: Props) => { + const { selectedChartId } = useWidgetStore(); + const [widgetName, setWidgetName] = useState("untited"); + const { setMeasurements, updateDuration, updateName } = useChartStore(); + const [duration, setDuration] = useState('1h') + const [dropDowndata, setDropDownData] = useState({}); + const [selections, setSelections] = useState>({}); + const { selectedZone } = useSelectedZoneStore(); + const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL; + const email = localStorage.getItem("email") || ""; + const organization = email?.split("@")[1]?.split(".")[0] + console.log(selectedChartId); + + + useEffect(() => { + const fetchZoneData = async () => { + try { + const response = await axios.get(`http://${iotApiUrl}/getinput`); + if (response.status === 200) { + // console.log("dropdown data:", response.data); + setDropDownData(response.data); + } else { + console.log("Unexpected response:", response); + } + } catch (error) { + console.error("There was an error!", error); + } + }; + fetchZoneData(); + }, []); + + useEffect(() => { + const fetchSavedInputes = async () => { + if (selectedChartId.id !== "") { + try { + const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/widget3D/${selectedChartId.id}/${organization}`); + if (response.status === 200) { + setSelections(response.data.Data.measurements) + setDuration(response.data.Data.duration) + setWidgetName(response.data.widgetName) + } else { + console.log("Unexpected response:", response); + } + } catch (error) { + console.error("There was an error!", error); + } + } + } + + fetchSavedInputes(); + + }, [selectedChartId.id]); + + // Sync Zustand state when component mounts + useEffect(() => { + setMeasurements(selections); + updateDuration(duration); + updateName(widgetName); + }, [selections, duration, widgetName]); + + + const sendInputes = async (inputMeasurement: any, inputDuration: any, inputName: any) => { + try { + const response = await axios.post(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/3dwidget/save`, { + organization: organization, + zoneId: selectedZone.zoneId, + widget: { + id: selectedChartId.id, + widgetName: inputName, + Data: { + measurements: inputMeasurement, + duration: inputDuration + } + } + } as any); + if (response.status === 200) { + return true + } else { + console.log("Unexpected response:", response); + return false + } + } catch (error) { + console.error("There was an error!", error); + return false + } + } + + const handleSelect = async (inputKey: string, selectedData: { name: string; fields: string } | null) => { + + // async() => { + const newSelections = { ...selections }; + if (selectedData === null) { + delete newSelections[inputKey]; + } else { + newSelections[inputKey] = selectedData; + } + // setMeasurements(newSelections); // Update Zustand store + console.log(newSelections); + if (await sendInputes(newSelections, duration, widgetName)) { + setSelections(newSelections); + } + // sendInputes(newSelections, duration); // Send data to server + // return newSelections; + // }; + }; + + const handleSelectDuration = async (option: string) => { + if (await sendInputes(selections, option, widgetName)) { + setDuration(option); + } + // setDuration(option); + }; + + const handleNameChange = async (name:any) => { + console.log('name change requested',name); + + if (await sendInputes(selections, duration, name)) { + setWidgetName(name); + } + } + + return ( + <> +
+
+
Title
+ +
+ {[...Array(2)].map((_, index) => { + const inputKey = `input${index + 1}`; + return ( +
+
Input {index + 1}
+
+ handleSelect(inputKey, selectedData)} + onUnselect={() => handleSelect(inputKey, null)} + selectedValue={selections[inputKey]} // Load from Zustand + /> +
+ +
+
+
+ ); + })} +
+ + ); +}; + +export default Widget2InputCard3D; \ No newline at end of file diff --git a/app/src/components/layout/sidebarRight/visualization/IotInputCards/Widget3InputCard3D.tsx b/app/src/components/layout/sidebarRight/visualization/IotInputCards/Widget3InputCard3D.tsx new file mode 100644 index 0000000..c720e66 --- /dev/null +++ b/app/src/components/layout/sidebarRight/visualization/IotInputCards/Widget3InputCard3D.tsx @@ -0,0 +1,158 @@ +import React, { useEffect, useState } from "react"; +import MultiLevelDropdown from "../../../../ui/inputs/MultiLevelDropDown"; +import { AddIcon } from "../../../../icons/ExportCommonIcons"; +import RegularDropDown from "../../../../ui/inputs/RegularDropDown"; +import useChartStore from "../../../../../store/useChartStore"; +import { useSelectedZoneStore } from "../../../../../store/useZoneStore"; +import { useWidgetStore } from "../../../../../store/useWidgetStore"; +import axios from "axios"; +import RenameInput from "../../../../ui/inputs/RenameInput"; + +const Widget3InputCard3D = () => { + const { selectedChartId } = useWidgetStore(); + const [widgetName, setWidgetName] = useState("untited"); + const { setMeasurements, updateDuration, updateName } = useChartStore(); + const [duration, setDuration] = useState('1h') + const [dropDowndata, setDropDownData] = useState({}); + const [selections, setSelections] = useState>({}); + const { selectedZone } = useSelectedZoneStore(); + const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL; + const email = localStorage.getItem("email") || ""; + const organization = email?.split("@")[1]?.split(".")[0] + console.log(selectedChartId); + + + useEffect(() => { + const fetchZoneData = async () => { + try { + const response = await axios.get(`http://${iotApiUrl}/getinput`); + if (response.status === 200) { + // console.log("dropdown data:", response.data); + setDropDownData(response.data); + } else { + console.log("Unexpected response:", response); + } + } catch (error) { + console.error("There was an error!", error); + } + }; + fetchZoneData(); + }, []); + + useEffect(() => { + const fetchSavedInputes = async () => { + if (selectedChartId.id !== "") { + try { + const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/widget3D/${selectedChartId.id}/${organization}`); + if (response.status === 200) { + setSelections(response.data.Data.measurements) + setDuration(response.data.Data.duration) + setWidgetName(response.data.widgetName) + } else { + console.log("Unexpected response:", response); + } + } catch (error) { + console.error("There was an error!", error); + } + } + } + + fetchSavedInputes(); + + }, [selectedChartId.id]); + + useEffect(() => { + setMeasurements(selections); + updateDuration(duration); + updateName(widgetName); + }, [selections, duration, widgetName]); + + + const sendInputes = async (inputMeasurement: any, inputDuration: any, inputName: any) => { + try { + const response = await axios.post(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/3dwidget/save`, { + organization: organization, + zoneId: selectedZone.zoneId, + widget: { + id: selectedChartId.id, + widgetName: inputName, + Data: { + measurements: inputMeasurement, + duration: inputDuration + } + } + } as any); + if (response.status === 200) { + return true + } else { + console.log("Unexpected response:", response); + return false + } + } catch (error) { + console.error("There was an error!", error); + return false + } + } + + const handleSelect = async (inputKey: string, selectedData: { name: string; fields: string } | null) => { + + // async() => { + const newSelections = { ...selections }; + if (selectedData === null) { + delete newSelections[inputKey]; + } else { + newSelections[inputKey] = selectedData; + } + // setMeasurements(newSelections); // Update Zustand store + console.log(newSelections); + if (await sendInputes(newSelections, duration, widgetName)) { + setSelections(newSelections); + } + }; + + const handleSelectDuration = async (option: string) => { + if (await sendInputes(selections, option, widgetName)) { + setDuration(option); + } + }; + + const handleNameChange = async (name:any) => { + console.log('name change requested',name); + + if (await sendInputes(selections, duration, name)) { + setWidgetName(name); + } + } + + return ( + <> +
+
+
Title
+ +
+ {[...Array(7)].map((_, index) => { + const inputKey = `input${index + 1}`; + return ( +
+
Input {index + 1}
+
+ handleSelect(inputKey, selectedData)} + onUnselect={() => handleSelect(inputKey, null)} + selectedValue={selections[inputKey]} // Load from Zustand + /> +
+ +
+
+
+ ); + })} +
+ + ); +}; + +export default Widget3InputCard3D; diff --git a/app/src/components/layout/sidebarRight/visualization/IotInputCards/Widget4InputCard3D.tsx b/app/src/components/layout/sidebarRight/visualization/IotInputCards/Widget4InputCard3D.tsx new file mode 100644 index 0000000..0be5489 --- /dev/null +++ b/app/src/components/layout/sidebarRight/visualization/IotInputCards/Widget4InputCard3D.tsx @@ -0,0 +1,178 @@ +import React, { useEffect, useState } from "react"; +import MultiLevelDropdown from "../../../../ui/inputs/MultiLevelDropDown"; +import { AddIcon } from "../../../../icons/ExportCommonIcons"; +import RegularDropDown from "../../../../ui/inputs/RegularDropDown"; +import useChartStore from "../../../../../store/useChartStore"; +import { useSelectedZoneStore } from "../../../../../store/useZoneStore"; +import { useWidgetStore } from "../../../../../store/useWidgetStore"; +import axios from "axios"; +import RenameInput from "../../../../ui/inputs/RenameInput"; + +type Props = {}; + +const Widget4InputCard3D = (props: Props) => { + const { selectedChartId } = useWidgetStore(); + const [widgetName, setWidgetName] = useState("untited"); + const { setMeasurements, updateDuration, updateName } = useChartStore(); + const [duration, setDuration] = useState('1h') + const [dropDowndata, setDropDownData] = useState({}); + const [selections, setSelections] = useState>({}); + const { selectedZone } = useSelectedZoneStore(); + const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL; + const email = localStorage.getItem("email") || ""; + const organization = email?.split("@")[1]?.split(".")[0] + console.log(selectedChartId); + + + useEffect(() => { + const fetchZoneData = async () => { + try { + const response = await axios.get(`http://${iotApiUrl}/getinput`); + if (response.status === 200) { + // console.log("dropdown data:", response.data); + setDropDownData(response.data); + } else { + console.log("Unexpected response:", response); + } + } catch (error) { + console.error("There was an error!", error); + } + }; + fetchZoneData(); + }, []); + + useEffect(() => { + const fetchSavedInputes = async () => { + if (selectedChartId.id !== "") { + try { + const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/widget3D/${selectedChartId.id}/${organization}`); + if (response.status === 200) { + setSelections(response.data.Data.measurements) + setDuration(response.data.Data.duration) + setWidgetName(response.data.widgetName) + } else { + console.log("Unexpected response:", response); + } + } catch (error) { + console.error("There was an error!", error); + } + } + } + + fetchSavedInputes(); + + }, [selectedChartId.id]); + + // Sync Zustand state when component mounts + useEffect(() => { + setMeasurements(selections); + updateDuration(duration); + updateName(widgetName); + }, [selections, duration, widgetName]); + + + const sendInputes = async (inputMeasurement: any, inputDuration: any, inputName: any) => { + try { + const response = await axios.post(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/3dwidget/save`, { + organization: organization, + zoneId: selectedZone.zoneId, + widget: { + id: selectedChartId.id, + widgetName: inputName, + Data: { + measurements: inputMeasurement, + duration: inputDuration + } + } + } as any); + if (response.status === 200) { + return true + } else { + console.log("Unexpected response:", response); + return false + } + } catch (error) { + console.error("There was an error!", error); + return false + } + } + + const handleSelect = async (inputKey: string, selectedData: { name: string; fields: string } | null) => { + + // async() => { + const newSelections = { ...selections }; + if (selectedData === null) { + delete newSelections[inputKey]; + } else { + newSelections[inputKey] = selectedData; + } + // setMeasurements(newSelections); // Update Zustand store + console.log(newSelections); + if (await sendInputes(newSelections, duration, widgetName)) { + setSelections(newSelections); + } + // sendInputes(newSelections, duration); // Send data to server + // return newSelections; + // }; + }; + + const handleSelectDuration = async (option: string) => { + if (await sendInputes(selections, option, widgetName)) { + setDuration(option); + } + // setDuration(option); + }; + + const handleNameChange = async (name:any) => { + console.log('name change requested',name); + + if (await sendInputes(selections, duration, name)) { + setWidgetName(name); + } + } + + return ( + <> +
+
+
Title
+ +
+ {[...Array(1)].map((_, index) => { + const inputKey = `input${index + 1}`; + return ( +
+
Input {index + 1}
+
+ handleSelect(inputKey, selectedData)} + onUnselect={() => handleSelect(inputKey, null)} + selectedValue={selections[inputKey]} // Load from Zustand + /> +
+ +
+
+
+ ); + })} +
+
+
+
Duration
+
+ +
+
+
+ + ); +}; + +export default Widget4InputCard3D; diff --git a/app/src/components/ui/componets/Dropped3dWidget.tsx b/app/src/components/ui/componets/Dropped3dWidget.tsx index 0a3534a..dbb1cba 100644 --- a/app/src/components/ui/componets/Dropped3dWidget.tsx +++ b/app/src/components/ui/componets/Dropped3dWidget.tsx @@ -129,13 +129,13 @@ export default function Dropped3dWidgets() { console.log('Typeeeeeeeeeeee',type); switch (type) { case "ui-Widget 1": - return ; + return ; case "ui-Widget 2": - return ; + return ; case "ui-Widget 3": - return ; + return ; case "ui-Widget 4": - return ; + return ; default: return null; } From 09963d3337b7319e82d7f0ab555b5ef1505eed00 Mon Sep 17 00:00:00 2001 From: gabriel Date: Wed, 2 Apr 2025 18:17:26 +0530 Subject: [PATCH 12/12] resolved conflict and merged to main --- app/src/store/store.ts | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/app/src/store/store.ts b/app/src/store/store.ts index 23af16c..87f937c 100644 --- a/app/src/store/store.ts +++ b/app/src/store/store.ts @@ -409,3 +409,22 @@ export const useWidgetSubOption = create((set: any) => ({ widgetSubOption: "2D", setWidgetSubOption: (x: any) => set({ widgetSubOption: x }), })); +export const useLimitDistance = create((set: any) => ({ + limitDistance: true, + setLimitDistance: (x: any) => set({ limitDistance: x }), +})); + +export const useTileDistance = create((set: any) => ({ + gridValue: { size: 300, divisions: 75 }, + planeValue: { height: 300, width: 300 }, + + setGridValue: (value: any) => + set((state: any) => ({ + gridValue: { ...state.gridValue, ...value }, + })), + + setPlaneValue: (value: any) => + set((state: any) => ({ + planeValue: { ...state.planeValue, ...value }, + })), +}));