Merge branch 'main' into rtViz

This commit is contained in:
Gomathi 2025-04-02 19:09:50 +05:30
commit 194cdbbbee
83 changed files with 5145 additions and 857 deletions

View File

@ -7,11 +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_REST_API_LOCAL_BASE_URL=192.168.0.102:5000
# 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 server marketplace API.
# REACT_APP_SERVER_MARKETPLACE_URL=185.100.212.76:50011
REACT_APP_SERVER_MARKETPLACE_URL=192.168.0.111:3501
# 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

View File

@ -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<ProductionCapacityProps> = ({ position }) => {
const ProductionCapacity : React.FC<ProductionCapacityProps> = ({ id, type, position }) => {
const { selectedChartId,setSelectedChartId } = useWidgetStore();
const { measurements: chartMeasurements, duration: chartDuration, name: widgetName } = useChartStore();
const [measurements, setmeasurements] = useState<any>({});
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,13 +99,94 @@ const ProductionCapacity: React.FC<ProductionCapacityProps> = ({ 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 (
<Html position={[position[0], position[1], position[2]] }
scale={[0.5, 0.5, 0.5]}
transform
zIndexRange={[1,0]}
sprite>
<div className="productionCapacity-wrapper card">
<Html position={[position[0], position[1], position[2]]}
scale={[0.5, 0.5, 0.5]}
transform
sprite>
<div className="productionCapacity-wrapper card"
onClick={
() => setSelectedChartId({
id: id,
type: type
})
}>
<div className="headeproductionCapacityr-wrapper">
<div className="header">Production Capacity</div>
<div className="production-capacity">
@ -105,7 +207,7 @@ const ProductionCapacity: React.FC<ProductionCapacityProps> = ({ position }) =>
</div>{" "}
<div className="bar-chart charts">
{/* Bar Chart */}
<Bar data={chartData} options={chartOptions} />
<Bar data={Object.keys(measurements).length > 0 ? chartData : defaultChartData } options={chartOptions} />
</div>
</div>
</Html>

View File

@ -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<SmoothLineGraphProps> = ({
return <Line data={data} options={options} />;
};
interface ReturnOfInvestmentProps {
id: string;
type: string;
position: [number, number, number];
}
const ReturnOfInvestment: React.FC<ReturnOfInvestmentProps> = ({ position }) => {
const ReturnOfInvestment: React.FC<ReturnOfInvestmentProps> = ({ id, type, position }) => {
const { selectedChartId,setSelectedChartId } = useWidgetStore();
const { measurements: chartMeasurements, duration: chartDuration, name: widgetName } = useChartStore();
const [measurements, setmeasurements] = useState<any>({});
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,17 +126,100 @@ const ReturnOfInvestment: React.FC<ReturnOfInvestmentProps> = ({ 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 (
<Html position={[position[0], position[1], position[2]]}
scale={[0.5, 0.5, 0.5]}
transform
zIndexRange={[1,0]}
sprite>
<div className="returnOfInvestment card">
<div className="returnOfInvestment card"
onClick={
() => setSelectedChartId({
id: id,
type: type
})
}>
<div className="header">Return of Investment</div>
<div className="lineGraph charts">
{/* Smooth curve graph with two datasets */}
<SmoothLineGraphComponent data={graphData} options={graphOptions} />
<SmoothLineGraphComponent data={Object.keys(measurements).length > 0 ? chartData : graphData} options={graphOptions} />
</div>
<div className="returns-wrapper">
<div className="icon">

View File

@ -1,29 +1,112 @@
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<StateWorkingProps> = ({ 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<StateWorkingProps> = ({ id, type, position }) => {
const { selectedChartId, setSelectedChartId } = useWidgetStore();
const { measurements: chartMeasurements, duration: chartDuration, name: widgetName } = useChartStore();
const [measurements, setmeasurements] = useState<any>({});
const [duration, setDuration] = useState("1h")
const [name, setName] = useState("Widget")
const [datas, setDatas] = useState<any>({});
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 (
<Html position={[position[0], position[1], position[2]]}
scale={[0.5, 0.5, 0.5]}
transform
zIndexRange={[1,0]}
sprite>
<div className="stateWorking-wrapper card">
<div className="stateWorking-wrapper card"
onClick={
() => setSelectedChartId({
id: id,
type: type
})
}>
<div className="header-wrapper">
<div className="header">
<span>State</span>
<span>
Working <span>.</span>
{datas?.input1 ? datas.input1 : 'input1'} <span>.</span>
</span>
</div>
<div className="img">
@ -32,12 +115,36 @@ const StateWorking: React.FC<StateWorkingProps> = ({ position }) => {
</div>
{/* Data */}
<div className="data-wrapper">
{datas.map((data, index) => (
{/* {datas.map((data, index) => (
<div className="data-table" key={index}>
<div className="data">{data.key}</div>
<div className="key">{data.value}</div>
</div>
))}
))} */}
<div className="data-table">
<div className="data">{measurements?.input2?.fields ? measurements.input2.fields : 'input2'}</div>
<div className="key">{datas?.input2 ? datas.input2 : 'data'}</div>
</div>
<div className="data-table">
<div className="data">{measurements?.input3?.fields ? measurements.input3.fields : 'input3'}</div>
<div className="key">{datas?.input3 ? datas.input3 : 'data'}</div>
</div>
<div className="data-table">
<div className="data">{measurements?.input4?.fields ? measurements.input4.fields : 'input4'}</div>
<div className="key">{datas?.input4 ? datas.input4 : 'data'}</div>
</div>
<div className="data-table">
<div className="data">{measurements?.input5?.fields ? measurements.input5.fields : 'input5'}</div>
<div className="key">{datas?.input5 ? datas.input5 : 'data'}</div>
</div>
<div className="data-table">
<div className="data">{measurements?.input6?.fields ? measurements.input6.fields : 'input6'}</div>
<div className="key">{datas?.input6 ? datas.input6 : 'data'}</div>
</div>
<div className="data-table">
<div className="data">{measurements?.input7?.fields ? measurements.input7.fields : 'input7'}</div>
<div className="key">{datas?.input7 ? datas.input7 : 'data'}</div>
</div>
</div>
</div>
</Html>

View File

@ -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<LineGraphProps> = ({ data, options }) => {
};
interface ThroughputProps {
id: string;
type: string;
position: [number, number, number];
}
const Throughput: React.FC<ThroughputProps> = ({ position }) => {
const Throughput: React.FC<ThroughputProps> = ({ id, type, position }) => {
const { selectedChartId,setSelectedChartId } = useWidgetStore();
const { measurements: chartMeasurements, duration: chartDuration, name: widgetName } = useChartStore();
const [measurements, setmeasurements] = useState<any>({});
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,14 +109,94 @@ const Throughput: React.FC<ThroughputProps> = ({ 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 (
<Html position={[position[0], position[1], position[2]]}
scale={[0.5, 0.5, 0.5]}
transform
zIndexRange={[1, 0]}
sprite>
<div className="throughput-wrapper">
<div className="header">Throughput</div>
<div className="throughput-wrapper"
onClick={
() => setSelectedChartId({
id: id,
type: type
})
}>
<div className="header">{name}</div>
<div className="display-value">
<div className="left">
<div className="icon">
@ -120,7 +219,7 @@ const Throughput: React.FC<ThroughputProps> = ({ position }) => {
</div>
<div className="line-graph">
{/* Line graph using react-chartjs-2 */}
<LineGraphComponent data={graphData} options={graphOptions} />
<LineGraphComponent data={Object.keys(measurements).length > 0 ? chartData : graphData} options={graphOptions} />
</div>
<div className="footer">
You made an extra <span className="value">$1256.13</span> this month

View File

@ -0,0 +1,32 @@
// ConfirmationDialog.tsx
import React from "react";
interface ConfirmationPopupProps {
message: string;
onConfirm: () => void;
onCancel: () => void;
}
const ConfirmationPopup: React.FC<ConfirmationPopupProps> = ({
message,
onConfirm,
onCancel,
}) => {
return (
<div className="confirmation-overlay">
<div className="confirmation-modal">
<p className="message">{message}</p>
<div className="buttton-wrapper">
<div className="confirmation-button" onClick={onConfirm}>
OK
</div>
<div className="confirmation-button" onClick={onCancel}>
Cancel
</div>
</div>
</div>
</div>
);
};
export default ConfirmationPopup;

View File

@ -9,13 +9,14 @@ 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";
import { useSelectedItem } from "../../../store/store";
interface AssetProp {
filename: string;
thumbnail?: string;
category: string;
description?: string;
tags?: string;
tags: string;
url?: String;
uploadDate?: number;
isArchieve?: boolean;
@ -23,19 +24,58 @@ 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<string>("");
const [selectedCategory, setSelectedCategory] = useState<string | null>(null);
const [filteredAsset, setFilteredAsset] = useState<AssetProp[]>([]);
const [categoryAssets, setCategoryAssets] = useState<AssetProp[]>([]);
const [filtereredAssets, setFiltereredAssets] = useState<AssetProp[]>([]);
const [categoryList, setCategoryList] = useState<CategoryListProp[]>([]);
const handleSearchChange = (value: string) => {
const searchTerm = value.toLowerCase();
setSearchValue(value);
setSelectedCategory(null); // Reset selected category when search changes
};
if (searchTerm.trim() === "" && !selectedCategory) {
setCategoryAssets([]);
return;
}
const filteredModels = filtereredAssets?.filter((model) => {
if (!model?.tags || !model?.filename || !model?.category) return false;
if (searchTerm.startsWith(":") && searchTerm.length > 1) {
const tagSearchTerm = searchTerm.slice(1);
return model.tags.toLowerCase().includes(tagSearchTerm);
} else if (selectedCategory) {
return (
model.category
.toLowerCase()
.includes(selectedCategory.toLowerCase()) &&
model.filename.toLowerCase().includes(searchTerm)
);
} else {
return model.filename.toLowerCase().includes(searchTerm);
}
});
const categoryList = useMemo(
() => [
setCategoryAssets(filteredModels);
};
useEffect(() => {
const filteredAssets = async () => {
try {
const filt = await fetchAssets();
setFiltereredAssets(filt);
} catch {}
};
filteredAssets();
}, [categoryAssets]);
useEffect(() => {
setCategoryList([
{
assetName: "Doors",
assetImage: "",
@ -58,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") {
@ -70,60 +108,93 @@ 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
} catch (error) { }
setCategoryAssets(res);
setFiltereredAssets(res);
} catch (error) {}
}
};
useEffect(() => { }, [filteredAsset]);
return (
<div className="assets-container">
<Search onChange={handleSearchChange} />
{searchValue ? (
<div className="searched-content">
<p>Results for "{searchValue}"</p>
<div className="assets-result">
<div className="assets-wrapper">
<div className="searched-content">
<p>Results for {searchValue}</p>
</div>
</div>
<div className="assets-container">
{categoryAssets &&
categoryAssets?.map((asset: any, index: number) => (
<div key={index} className="assets">
<img
src={asset?.thumbnail}
alt={asset.filename}
className="asset-image"
/>
<div className="asset-name">
{asset.filename
.split("_")
.map(
(word: any) =>
word.charAt(0).toUpperCase() + word.slice(1)
)
.join(" ")}
</div>
</div>
))}
</div>
</div>
) : selectedCategory ? (
<div className="assets-wrapper">
{/* Back Button */}
<div
className="back-button"
onClick={() => {
setSelectedCategory(null);
setFilteredAsset([]);
setCategoryAssets([]);
}}
>
Back
</div>
<h2>{selectedCategory}</h2>
<div className="assets-container">
{filteredAsset &&
filteredAsset?.map((asset: any, index: number) => (
{categoryAssets &&
categoryAssets?.map((asset: any, index: number) => (
<div key={index} className="assets">
{asset?.thumbnail && (
<img
src={asset?.thumbnail}
alt={asset.filename}
className="asset-image"
onPointerDown={() => setSelectedItem({ name: asset.filename, id: asset.modelfileID })}
/>
)}
<img
src={asset?.thumbnail}
alt={asset.filename}
className="asset-image"
onPointerDown={() =>
setSelectedItem({
name: asset.filename,
id: asset.modelfileID,
})
}
/>
<div className="asset-name">
{asset.filename
.split("_")

View File

@ -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,6 +101,7 @@ const ProgressBarWidget = ({
</div>
);
};
console.log(chartTypes, "chartTypes");
const Widgets2D = () => {
return (
@ -123,6 +126,7 @@ const Widgets2D = () => {
{ key: "units", value: 1000, description: "Initial stock" },
],
}}
type={"progress 1"}
/>
<ProgressBarWidget
id="widget-8"
@ -133,6 +137,7 @@ const Widgets2D = () => {
{ key: "units", value: 500, description: "Additional stock" },
],
}}
type={"progress 2"}
/>
</div>
</div>

View File

@ -33,6 +33,7 @@ const RenderAnalysisInputs: React.FC<InputRendererProps> = ({
label={preset.inputs.label}
min={0}
max={0}
value={5}
/>
);
}

View File

@ -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,17 +6,20 @@ import LabeledButton from "../../../ui/inputs/LabledButton";
import {
useAzimuth,
useElevation,
useLimitDistance,
useRenderDistance,
useResetCamera,
useRoofVisibility,
useSelectedWallItem,
useShadows,
useSocketStore,
useTileDistance,
useToggleView,
useWallVisibility,
} 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,26 +30,95 @@ 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] = useState(false);
const [distance, setDistance] = useState<number>(30);
const { limitDistance, setLimitDistance } = useLimitDistance();
const [distance, setDistance] = useState<number>(40);
useEffect(() => {}, [limitDistance]);
const [limitGridDistance, setLimitGridDistance] = useState(false);
const [gridDistance, setGridDistance] = useState<number>(5);
const [gridDistance, setGridDistance] = useState<number>(3);
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) {
setDistance(value);
setRenderDistance(value);
}
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";
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");
@ -58,7 +130,9 @@ const GlobalProperties: React.FC = () => {
localStorage.getItem("userId")!,
wallVisibility,
!roofVisibility,
shadows
shadows,
renderDistance,
limitDistance
);
//
@ -85,7 +159,9 @@ const GlobalProperties: React.FC = () => {
localStorage.getItem("userId")!,
!wallVisibility,
roofVisibility,
shadows
shadows,
renderDistance,
limitDistance
);
//
@ -112,7 +188,9 @@ const GlobalProperties: React.FC = () => {
localStorage.getItem("userId")!,
wallVisibility,
roofVisibility,
!shadows
!shadows,
renderDistance,
limitDistance
);
//
@ -184,18 +262,24 @@ 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
}}
/>
<InputRange
label="Distance"
disabled={!limitDistance}
value={distance}
key={"5"}
value={renderDistance}
min={CONSTANTS.distanceConfig.minDistance}
max={CONSTANTS.distanceConfig.maxDistance}
onChange={(value: number) => updateDistance(value)}
onPointerUp={updatedDist}
key={"6"}
/>
<div className="split"></div>
@ -213,7 +297,10 @@ const GlobalProperties: React.FC = () => {
disabled={!limitGridDistance}
value={gridDistance}
key={"7"}
min={1}
max={5}
onChange={(value: number) => updateGridDistance(value)}
onPointerUp={updatedGrid}
/>
</div>
);

View File

@ -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<Record<string, { name: string; fields: string }>>({});
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 (
<>
<div className="inputs-wrapper">
<div className="datas">
<div className="datas__label">Title</div>
<RenameInput value={selectedChartId?.title || "untited"} onRename={handleNameChange}/>
</div>
{[...Array(3)].map((_, index) => {
const inputKey = `input${index + 1}`;
return (
<div key={index} className="datas">
<div className="datas__label">Input {index + 1}</div>
<div className="datas__class">
<MultiLevelDropdown
data={dropDowndata}
onSelect={(selectedData) => handleSelect(inputKey, selectedData)}
onUnselect={() => handleSelect(inputKey, null)}
selectedValue={selections[inputKey]} // Load from Zustand
/>
<div className="icon">
<AddIcon />
</div>
</div>
</div>
);
})}
</div>
<div>
<div className="datas">
<div className="datas__label">Duration</div>
<div className="datas__class">
<RegularDropDown
header={duration}
options={["1h", "2h", "12h"]}
onSelect={handleSelectDuration}
search={false}
/>
</div>
</div>
</div>
</>
);
};
export default BarChartInput;

View File

@ -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<Record<string, { name: string; fields: string }>>({});
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 (
<>
<div className="inputs-wrapper">
<div className="datas">
<div className="datas__label">Title</div>
<RenameInput value={selectedChartId?.header || "untited"} onRename={handleNameChange}/>
</div>
{[...Array(1)].map((_, index) => {
const inputKey = `input${index + 1}`;
return (
<div key={index} className="datas">
<div className="datas__label">Input {index + 1}</div>
<div className="datas__class">
<MultiLevelDropdown
data={dropDowndata}
onSelect={(selectedData) => handleSelect(inputKey, selectedData)}
onUnselect={() => handleSelect(inputKey, null)}
selectedValue={selections[inputKey]} // Load from Zustand
/>
<div className="icon">
<AddIcon />
</div>
</div>
</div>
);
})}
</div>
<div>
{/* <div className="datas">
<div className="datas__label">Duration</div>
<div className="datas__class">
<RegularDropDown
header={duration}
options={["1h", "2h", "12h"]}
onSelect={handleSelectDuration}
search={false}
/>
</div>
</div> */}
</div>
</>
);
};
export default FleetEfficiencyInputComponent;

View File

@ -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<Record<string, { name: string; fields: string }>>({});
@ -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) => {
<div className="inputs-wrapper">
<div className="datas">
<div className="datas__label">Title</div>
<RenameInput value={selectedChartId?.title || "untited"} onRename={handleNameChange}/>
<RenameInput value={selectedChartId?.header || "untited"} onRename={handleNameChange}/>
</div>
{[...Array(6)].map((_, index) => {
const inputKey = `input${index + 1}`;

View File

@ -0,0 +1,153 @@
import React from 'react'
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 Widget2InputCard3D from './Widget2InputCard3D'
import Widget3InputCard3D from './Widget3InputCard3D'
import Widget4InputCard3D from './Widget4InputCard3D'
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 (
<>
<div className="sideBarHeader">2D Widget Input</div>
<BarChartInput />
</>
)
}
else if (selectedChartId && selectedChartId.type && selectedChartId.type === 'line' ) {
return (
<>
<div className="sideBarHeader">2D Widget Input</div>
<LineGrapInput />
</>
)
}
else if (selectedChartId && selectedChartId.type && selectedChartId.type === 'pie' ) {
return (
<>
<div className="sideBarHeader">2D Widget Input</div>
<PieChartInput />
</>
)
}
else if (selectedChartId && selectedChartId.type && selectedChartId.type === 'doughnut' ) {
return (
<>
<div className="sideBarHeader">2D Widget Input</div>
<PieChartInput />
</>
)
}
else if (selectedChartId && selectedChartId.type && selectedChartId.type === 'polarArea' ) {
return (
<>
<div className="sideBarHeader">2D Widget Input</div>
<PieChartInput />
</>
)
}
else if (selectedChartId && selectedChartId.type && selectedChartId.type === 'progress 1' ) {
return (
<>
<div className="sideBarHeader">2D Widget Input</div>
<Progress1Input />
</>
)
}
else if (selectedChartId && selectedChartId.type && selectedChartId.type === 'progress 2' ) {
return (
<>
<div className="sideBarHeader">2D Widget Input</div>
<Progress2Input />
</>
)
}
else if (selectedChartId && selectedChartId.className && selectedChartId.className === 'warehouseThroughput floating' ) {
return (
<>
<div className="sideBarHeader">Floting Widget Input</div>
<WarehouseThroughputInputComponent />
</>
)
}
else if (selectedChartId && selectedChartId.className && selectedChartId.className === 'fleetEfficiency floating' ) {
return (
<>
<div className="sideBarHeader">Floting Widget Input</div>
<FleetEfficiencyInputComponent />
</>
)
}
else if (selectedChartId && selectedChartId.className && selectedChartId.className === 'floating total-card' ) {
return (
<>
<div className="sideBarHeader">Floting Widget Input</div>
<FleetEfficiencyInputComponent />
</>
)
}
else if (selectedChartId && selectedChartId.type && selectedChartId.type === 'ui-Widget 1' ) {
return (
<>
<div className="sideBarHeader">3D Widget Input</div>
<Widget4InputCard3D />
</>
)
}
else if (selectedChartId && selectedChartId.type && selectedChartId.type === 'ui-Widget 2' ) {
return (
<>
<div className="sideBarHeader">3D Widget Input</div>
<Widget2InputCard3D />
</>
)
}
else if (selectedChartId && selectedChartId.type && selectedChartId.type === 'ui-Widget 3' ) {
return (
<>
<div className="sideBarHeader">3D Widget Input</div>
<Widget3InputCard3D />
</>
)
}
else if (selectedChartId && selectedChartId.type && selectedChartId.type === 'ui-Widget 4' ) {
return (
<>
<div className="sideBarHeader">3D Widget Input</div>
<Widget4InputCard3D />
</>
)
}
else {
return (
<div>No chart selected</div>
)
}
}
export default InputSelecterComponent

View File

@ -256,7 +256,7 @@ const LineGrapInput = (props: Props) => {
<div className="datas__label">Title</div>
<RenameInput value={selectedChartId?.title || "untited"} onRename={handleNameChange}/>
</div>
{[...Array(6)].map((_, index) => {
{[...Array(4)].map((_, index) => {
const inputKey = `input${index + 1}`;
return (
<div key={index} className="datas">

View File

@ -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<Record<string, {name: string, fields: string}>>({})
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<Record<string, { name: string; fields: string }>>({});
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 (
<>
<div className="inputs-wrapper">
{[...Array(3)].map((_, index) => {
const inputKey = `input${index+1}`;
return (
<div key={index} className="datas">
<div className="datas__label">Input {index+1}</div>
<div className="datas__class">
<MultiLevelDropdown
data={dropDowndata}
onSelect={(selectedData) => handleSelect(inputKey, selectedData)}
onUnselect={() => handleSelect(inputKey, null)}
selectedValue={selections[inputKey]}
/>
<div className="icon">
<AddIcon />
</div>
</div>
</div>
);
})}
</div>
<div>
</div>
</>
)
}
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
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 (
<>
<div className="inputs-wrapper">
<div className="datas">
<div className="datas__label">Title</div>
<RenameInput value={selectedChartId?.title || "untited"} onRename={handleNameChange}/>
</div>
{[...Array(2)].map((_, index) => {
const inputKey = `input${index + 1}`;
return (
<div key={index} className="datas">
<div className="datas__label">Input {index + 1}</div>
<div className="datas__class">
<MultiLevelDropdown
data={dropDowndata}
onSelect={(selectedData) => handleSelect(inputKey, selectedData)}
onUnselect={() => handleSelect(inputKey, null)}
selectedValue={selections[inputKey]} // Load from Zustand
/>
<div className="icon">
<AddIcon />
</div>
</div>
</div>
);
})}
</div>
<div>
<div className="datas">
<div className="datas__label">Duration</div>
<div className="datas__class">
<RegularDropDown
header={duration}
options={["1h", "2h", "12h"]}
onSelect={handleSelectDuration}
search={false}
/>
</div>
</div>
</div>
</>
);
};
export default PieChartInput;

View File

@ -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<Record<string, { name: string; fields: string }>>({});
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 (
<>
<div className="inputs-wrapper">
<div className="datas">
<div className="datas__label">Title</div>
<RenameInput value={selectedChartId?.title || "untited"} onRename={handleNameChange}/>
</div>
{[...Array(1)].map((_, index) => {
const inputKey = `input${index + 1}`;
return (
<div key={index} className="datas">
<div className="datas__label">Input {index + 1}</div>
<div className="datas__class">
<MultiLevelDropdown
data={dropDowndata}
onSelect={(selectedData) => handleSelect(inputKey, selectedData)}
onUnselect={() => handleSelect(inputKey, null)}
selectedValue={selections[inputKey]} // Load from Zustand
/>
<div className="icon">
<AddIcon />
</div>
</div>
</div>
);
})}
</div>
{/* <div>
<div className="datas">
<div className="datas__label">Duration</div>
<div className="datas__class">
<RegularDropDown
header={duration}
options={["1h", "2h", "12h"]}
onSelect={handleSelectDuration}
search={false}
/>
</div>
</div>
</div> */}
</>
);
};
export default Progress1Input;

View File

@ -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<Record<string, { name: string; fields: string }>>({});
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 (
<>
<div className="inputs-wrapper">
<div className="datas">
<div className="datas__label">Title</div>
<RenameInput value={selectedChartId?.title || "untited"} onRename={handleNameChange}/>
</div>
{[...Array(2)].map((_, index) => {
const inputKey = `input${index + 1}`;
return (
<div key={index} className="datas">
<div className="datas__label">Input {index + 1}</div>
<div className="datas__class">
<MultiLevelDropdown
data={dropDowndata}
onSelect={(selectedData) => handleSelect(inputKey, selectedData)}
onUnselect={() => handleSelect(inputKey, null)}
selectedValue={selections[inputKey]} // Load from Zustand
/>
<div className="icon">
<AddIcon />
</div>
</div>
</div>
);
})}
</div>
{/* <div>
<div className="datas">
<div className="datas__label">Duration</div>
<div className="datas__class">
<RegularDropDown
header={duration}
options={["1h", "2h", "12h"]}
onSelect={handleSelectDuration}
search={false}
/>
</div>
</div>
</div> */}
</>
);
};
export default Progress2Input;

View File

@ -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<Record<string, { name: string; fields: string }>>({});
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 (
<>
<div className="inputs-wrapper">
<div className="datas">
<div className="datas__label">Title</div>
<RenameInput value={selectedChartId?.header || "untited"} onRename={handleNameChange}/>
</div>
{[...Array(1)].map((_, index) => {
const inputKey = `input${index + 1}`;
return (
<div key={index} className="datas">
<div className="datas__label">Input {index + 1}</div>
<div className="datas__class">
<MultiLevelDropdown
data={dropDowndata}
onSelect={(selectedData) => handleSelect(inputKey, selectedData)}
onUnselect={() => handleSelect(inputKey, null)}
selectedValue={selections[inputKey]} // Load from Zustand
/>
<div className="icon">
<AddIcon />
</div>
</div>
</div>
);
})}
</div>
<div>
<div className="datas">
<div className="datas__label">Duration</div>
<div className="datas__class">
<RegularDropDown
header={duration}
options={["1h", "2h", "12h"]}
onSelect={handleSelectDuration}
search={false}
/>
</div>
</div>
</div>
</>
);
};
export default WarehouseThroughputInputComponent;

View File

@ -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<Record<string, { name: string; fields: string }>>({});
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 (
<>
<div className="inputs-wrapper">
<div className="datas">
<div className="datas__label">Title</div>
<RenameInput value={widgetName} onRename={handleNameChange}/>
</div>
{[...Array(2)].map((_, index) => {
const inputKey = `input${index + 1}`;
return (
<div key={index} className="datas">
<div className="datas__label">Input {index + 1}</div>
<div className="datas__class">
<MultiLevelDropdown
data={dropDowndata}
onSelect={(selectedData) => handleSelect(inputKey, selectedData)}
onUnselect={() => handleSelect(inputKey, null)}
selectedValue={selections[inputKey]} // Load from Zustand
/>
<div className="icon">
<AddIcon />
</div>
</div>
</div>
);
})}
</div>
</>
);
};
export default Widget2InputCard3D;

View File

@ -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<Record<string, { name: string; fields: string }>>({});
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 (
<>
<div className="inputs-wrapper">
<div className="datas">
<div className="datas__label">Title</div>
<RenameInput value={widgetName} onRename={handleNameChange}/>
</div>
{[...Array(7)].map((_, index) => {
const inputKey = `input${index + 1}`;
return (
<div key={index} className="datas">
<div className="datas__label">Input {index + 1}</div>
<div className="datas__class">
<MultiLevelDropdown
data={dropDowndata}
onSelect={(selectedData) => handleSelect(inputKey, selectedData)}
onUnselect={() => handleSelect(inputKey, null)}
selectedValue={selections[inputKey]} // Load from Zustand
/>
<div className="icon">
<AddIcon />
</div>
</div>
</div>
);
})}
</div>
</>
);
};
export default Widget3InputCard3D;

View File

@ -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<Record<string, { name: string; fields: string }>>({});
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 (
<>
<div className="inputs-wrapper">
<div className="datas">
<div className="datas__label">Title</div>
<RenameInput value={widgetName} onRename={handleNameChange}/>
</div>
{[...Array(1)].map((_, index) => {
const inputKey = `input${index + 1}`;
return (
<div key={index} className="datas">
<div className="datas__label">Input {index + 1}</div>
<div className="datas__class">
<MultiLevelDropdown
data={dropDowndata}
onSelect={(selectedData) => handleSelect(inputKey, selectedData)}
onUnselect={() => handleSelect(inputKey, null)}
selectedValue={selections[inputKey]} // Load from Zustand
/>
<div className="icon">
<AddIcon />
</div>
</div>
</div>
);
})}
</div>
<div>
<div className="datas">
<div className="datas__label">Duration</div>
<div className="datas__class">
<RegularDropDown
header={duration}
options={["1h", "2h", "12h"]}
onSelect={handleSelectDuration}
search={false}
/>
</div>
</div>
</div>
</>
);
};
export default Widget4InputCard3D;

View File

@ -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 = {
@ -118,7 +119,7 @@ const Data = () => {
};
});
};
console.log("selectedChartId", selectedChartId);
return (
<div className="dataSideBar">
@ -133,8 +134,7 @@ const Data = () => {
{
chartDataGroups[selectedChartId?.id] &&
<>
<div className="sideBarHeader">2D Widget Input</div>
<LineGrapInput />
<InputSelecterComponent />
</>
}

View File

@ -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<CollaborateProps> = ({
setUserManagement,
}) => {
const { activeUsers } = useActiveUsers();
useEffect(() => {
console.log("activeUsers: ", activeUsers);
}, [activeUsers]);
const userName = localStorage.getItem("userName") || "Anonymous";
const users = [
{

View File

@ -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 {
@ -73,7 +73,8 @@ const DisplayZone: React.FC<DisplayZoneProps> = ({
// State to track overflow visibility
const [showLeftArrow, setShowLeftArrow] = useState(false);
const [showRightArrow, setShowRightArrow] = useState(false);
const { floatingWidget, setFloatingWidget } = useFloatingWidget();
const { floatingWidget, setFloatingWidget } = useFloatingWidget()
const{setSelectedChartId}=useWidgetStore()
// Function to calculate overflow state
const updateOverflowState = useCallback(() => {
@ -150,6 +151,7 @@ const DisplayZone: React.FC<DisplayZoneProps> = ({
if (selectedZone?.zoneId === zoneId) {
return;
}
setSelectedChartId(null)
const email = localStorage.getItem("email") || "";
const organization = email?.split("@")[1]?.split(".")[0];
let response = await getSelect2dZoneData(zoneId, organization);

View File

@ -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,
@ -13,6 +15,7 @@ import {
import { useEffect, useRef, useState } from "react";
import { duplicateWidgetApi } from "../../../services/realTimeVisulization/zoneData/duplicateWidget";
import { deleteWidgetApi } from "../../../services/realTimeVisulization/zoneData/deleteWidgetApi";
import { useClickOutside } from "./functions/handleWidgetsOuterClick";
import { useSocketStore } from "../../../store/store";
type Side = "top" | "bottom" | "left" | "right";
@ -81,6 +84,8 @@ export const DraggableWidget = ({
}
};
const chartWidget = useRef<HTMLDivElement>(null);
const isPanelHidden = hiddenPanels.includes(widget.panel);
const deleteSelectedChart = async () => {
@ -117,13 +122,11 @@ export const DraggableWidget = ({
// }));
// }
} catch (error) {
} finally {
setOpenKebabId(null);
}
};
const getCurrentWidgetCount = (panel: Side) =>
selectedZone.widgets.filter((w) => w.panel === panel).length;
@ -183,13 +186,11 @@ export const DraggableWidget = ({
// }));
// }
} catch (error) {
} finally {
setOpenKebabId(null);
}
};
const handleKebabClick = (event: React.MouseEvent<HTMLDivElement>) => {
event.stopPropagation();
if (openKebabId === widget.id) {
@ -237,23 +238,31 @@ export const DraggableWidget = ({
onReorder(fromIndex, toIndex); // Call the reorder function passed as a prop
}
};
console.log("widget.type", widget.type);
// useClickOutside(chartWidget, () => {
// setSelectedChartId(null);
// });
console.log('isPanelHidden: ', isPanelHidden);
return (
<>
<div
draggable
key={widget.id}
className={`chart-container ${selectedChartId?.id === widget.id && "activeChart"
}`}
className={`chart-container ${
selectedChartId?.id === widget.id && "activeChart"
}`}
onPointerDown={handlePointerDown}
onDragStart={handleDragStart}
onDragEnter={handleDragEnter}
onDragOver={handleDragOver}
onDrop={handleDrop}
style={{
opacity: isPanelHidden ? 0 : 1,
pointerEvents: isPanelHidden ? "none" : "auto",
}}
ref={chartWidget}
onClick={() => setSelectedChartId(widget)}
>
{/* Kebab Icon */}
<div className="icon kebab" onClick={handleKebabClick}>
@ -264,8 +273,9 @@ export const DraggableWidget = ({
{openKebabId === widget.id && (
<div className="kebab-options" ref={widgetRef}>
<div
className={`edit btn ${isPanelFull(widget.panel) ? "btn-blur" : ""
}`}
className={`edit btn ${
isPanelFull(widget.panel) ? "btn-blur" : ""
}`}
onClick={isPanelFull(widget.panel) ? undefined : duplicateWidget}
>
<div className="icon">
@ -284,8 +294,11 @@ export const DraggableWidget = ({
{/* Render charts based on widget type */}
{widget.type === "progress" && (
<ProgressCard title={widget.title} data={widget.data} />
{widget.type === "progress 1" && (
<ProgressCard1 title={widget.title} id={widget.id} />
)}
{widget.type === "progress 2" && (
<ProgressCard2 title={widget.title} id={widget.id} />
)}
{widget.type === "line" && (
<LineGraphComponent
@ -294,14 +307,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 === "bar" && (
@ -311,14 +316,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" && (
@ -328,14 +325,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" && (

View File

@ -1,4 +1,3 @@
import { useThree } from "@react-three/fiber";
import React, { useState, useEffect } from "react";
import { useAsset3dWidget, useSocketStore, useWidgetSubOption } from "../../../store/store";
@ -32,8 +31,8 @@ export default function Dropped3dWidgets() {
if (activeModule !== "visualization") return;
if (!selectedZone.zoneId) return;
const email = localStorage.getItem("email") || "";
const organization = email?.split("@")[1]?.split(".")[0];
const email = localStorage.getItem("email") || "";
const organization = email?.split("@")[1]?.split(".")[0];
async function get3dWidgetData() {
let result = await get3dWidgetZoneData(selectedZone.zoneId, organization);
@ -115,15 +114,16 @@ export default function Dropped3dWidgets() {
return (
<>
{activeZoneWidgets.map(({ id, type, position }) => {
console.log('Typeeeeeeeeeeee',type);
switch (type) {
case "ui-Widget 1":
return <ProductionCapacity key={id} position={position} />;
return <ProductionCapacity key={id} id={id} type={type} position={position} />;
case "ui-Widget 2":
return <ReturnOfInvestment key={id} position={position} />;
return <ReturnOfInvestment key={id} id={id} type={type} position={position} />;
case "ui-Widget 3":
return <StateWorking key={id} position={position} />;
return <StateWorking key={id} id={id} type={type} position={position} />;
case "ui-Widget 4":
return <Throughput key={id} position={position} />;
return <Throughput key={id} id={id} type={type} position={position} />;
default:
return null;
}
@ -131,4 +131,3 @@ export default function Dropped3dWidgets() {
</>
);
}

View File

@ -1,4 +1,3 @@
import { WalletIcon } from "../../icons/3dChartIcons";
import { useEffect, useRef, useState } from "react";
import {
@ -22,6 +21,9 @@ import WarehouseThroughputComponent from "../realTimeVis/floating/WarehouseThrou
import FleetEfficiencyComponent from "../realTimeVis/floating/FleetEfficiencyComponent";
import { useWidgetStore } from "../../../store/useWidgetStore";
import { useSocketStore } from "../../../store/store";
import { useClickOutside } from "./functions/handleWidgetsOuterClick";
import { usePlayButtonStore } from "../../../store/usePlayButtonStore";
import { useSelectedZoneStore } from "../../../store/useZoneStore";
interface DraggingState {
zone: string;
index: number;
@ -45,19 +47,24 @@ interface DraggingState {
}
const DroppedObjects: React.FC = () => {
const { visualizationSocket } = useSocketStore();
const { isPlaying } = usePlayButtonStore();
const zones = useDroppedObjectsStore((state) => state.zones);
const [openKebabId, setOpenKebabId] = useState<string | null>(null);
const updateObjectPosition = useDroppedObjectsStore(
(state) => state.updateObjectPosition
);
const { setSelectedZone, selectedZone } = useSelectedZoneStore();
const deleteObject = useDroppedObjectsStore((state) => state.deleteObject);
const duplicateObject = useDroppedObjectsStore((state) => state.duplicateObject);
const duplicateObject = useDroppedObjectsStore(
(state) => state.duplicateObject
);
const [draggingIndex, setDraggingIndex] = useState<DraggingState | null>(
null
);
const [offset, setOffset] = useState<[number, number] | null>(null);
const { setSelectedChartId } = useWidgetStore();
const { selectedChartId, setSelectedChartId } = useWidgetStore();
const [activeEdges, setActiveEdges] = useState<{
vertical: "top" | "bottom";
horizontal: "left" | "right";
@ -70,6 +77,11 @@ const DroppedObjects: React.FC = () => {
} | null>(null); // State to track the current position during drag
const animationRef = useRef<number | null>(null);
const { activeModule } = useModuleStore();
const chartWidget = useRef<HTMLDivElement>(null);
// useClickOutside(chartWidget, () => {
// setSelectedChartId(null);
// });
const kebabRef = useRef<HTMLDivElement>(null);
@ -103,7 +115,7 @@ const DroppedObjects: React.FC = () => {
function handleDuplicate(zoneName: string, index: number) {
setOpenKebabId(null)
setOpenKebabId(null);
duplicateObject(zoneName, index); // Call the duplicateObject method from the store
}
@ -181,11 +193,150 @@ const DroppedObjects: React.FC = () => {
}
setOffset([offsetY, offsetX]);
// Add native event listeners for smoother tracking
const handlePointerMoveNative = (e: PointerEvent) => {
if (!draggingIndex || !offset) return;
if (isPlaying === true) return;
const rect = container.getBoundingClientRect();
const relativeX = e.clientX - rect.left;
const relativeY = e.clientY - rect.top;
// Use requestAnimationFrame for smooth updates
animationRef.current = requestAnimationFrame(() => {
// Dynamically determine the current position strategy
const newPositionStrategy = determinePosition(
rect,
relativeX,
relativeY
);
const [activeProp1, activeProp2] =
getActiveProperties(newPositionStrategy);
// Update active edges for distance lines
const vertical = activeProp1 === "top" ? "top" : "bottom";
const horizontal = activeProp2 === "left" ? "left" : "right";
setActiveEdges({ vertical, horizontal });
// Calculate new position based on the active properties
let newY = 0;
let newX = 0;
if (activeProp1 === "top") {
newY = relativeY - offset[0];
} else {
newY = rect.height - (relativeY + offset[0]);
}
if (activeProp2 === "left") {
newX = relativeX - offset[1];
} else {
newX = rect.width - (relativeX + offset[1]);
}
// Apply boundaries
newX = Math.max(0, Math.min(rect.width - 50, newX));
newY = Math.max(0, Math.min(rect.height - 50, newY));
// Create new position object
const newPosition = {
...newPositionStrategy,
[activeProp1]: newY,
[activeProp2]: newX,
// Clear opposite properties
[activeProp1 === "top" ? "bottom" : "top"]: "auto",
[activeProp2 === "left" ? "right" : "left"]: "auto",
};
// Update the current position state for DistanceLines
setCurrentPosition(newPosition);
updateObjectPosition(zoneName, draggingIndex.index, newPosition);
});
};
const handlePointerUpNative = async (e: PointerEvent) => {
// Clean up native event listeners
element.removeEventListener("pointermove", handlePointerMoveNative);
element.removeEventListener("pointerup", handlePointerUpNative);
element.releasePointerCapture(e.pointerId);
if (!draggingIndex || !offset) return;
const rect = container.getBoundingClientRect();
const relativeX = e.clientX - rect.left;
const relativeY = e.clientY - rect.top;
// Determine final position strategy
const finalPosition = determinePosition(rect, relativeX, relativeY);
const [activeProp1, activeProp2] = getActiveProperties(finalPosition);
// Calculate final position
let finalY = 0;
let finalX = 0;
if (activeProp1 === "top") {
finalY = relativeY - offset[0];
} else {
finalY = rect.height - (relativeY + offset[0]);
}
if (activeProp2 === "left") {
finalX = relativeX - offset[1];
} else {
finalX = rect.width - (relativeX + offset[1]);
}
// Apply boundaries
finalX = Math.max(0, Math.min(rect.width - 50, finalX));
finalY = Math.max(0, Math.min(rect.height - 50, finalY));
const boundedPosition = {
...finalPosition,
[activeProp1]: finalY,
[activeProp2]: finalX,
[activeProp1 === "top" ? "bottom" : "top"]: "auto",
[activeProp2 === "left" ? "right" : "left"]: "auto",
};
try {
// Save to backend
const email = localStorage.getItem("email") || "";
const organization = email?.split("@")[1]?.split(".")[0];
const response = await addingFloatingWidgets(
zone.zoneId,
organization,
{
...zone.objects[draggingIndex.index],
position: boundedPosition,
}
);
if (response.message === "Widget updated successfully") {
updateObjectPosition(zoneName, draggingIndex.index, boundedPosition);
}
} catch (error) {
} finally {
setDraggingIndex(null);
setOffset(null);
setActiveEdges(null);
setCurrentPosition(null);
if (animationRef.current) {
cancelAnimationFrame(animationRef.current);
animationRef.current = null;
}
}
};
// Add native event listeners
element.addEventListener("pointermove", handlePointerMoveNative);
element.addEventListener("pointerup", handlePointerUpNative);
};
const handlePointerMove = (event: React.PointerEvent) => {
if (!draggingIndex || !offset) return;
if (isPlaying === true) return;
const container = document.getElementById("real-time-vis-canvas");
if (!container) return;
@ -248,6 +399,7 @@ const DroppedObjects: React.FC = () => {
const handlePointerUp = async (event: React.PointerEvent<HTMLDivElement>) => {
try {
if (!draggingIndex || !offset) return;
if (isPlaying === true) return;
const container = document.getElementById("real-time-vis-canvas");
if (!container) return;
@ -256,11 +408,11 @@ const DroppedObjects: React.FC = () => {
const relativeX = event.clientX - rect.left;
const relativeY = event.clientY - rect.top;
// Only now determine the final position strategy
// Determine final position strategy
const finalPosition = determinePosition(rect, relativeX, relativeY);
const [activeProp1, activeProp2] = getActiveProperties(finalPosition);
// Calculate final position using the new strategy
// Calculate final position
let finalY = 0;
let finalX = 0;
@ -357,29 +509,48 @@ const DroppedObjects: React.FC = () => {
{zone.objects.map((obj, index) => (
<div
key={`${zoneName}-${index}`}
className={obj.className}
className={`${obj.className} ${
selectedChartId?.id === obj.id && "activeChart"
}`}
ref={chartWidget}
style={{
position: "absolute",
top:
typeof obj.position.top === "number"
? `${obj.position.top}px`
? `calc(${obj.position.top}px + ${
isPlaying && selectedZone.activeSides.includes("top")
? 90
: 0
}px)`
: "auto",
left:
typeof obj.position.left === "number"
? `${obj.position.left}px`
? `calc(${obj.position.left}px + ${
isPlaying && selectedZone.activeSides.includes("left")
? 90
: 0
}px)`
: "auto",
right:
typeof obj.position.right === "number"
? `${obj.position.right}px`
? `calc(${obj.position.right}px + ${
isPlaying && selectedZone.activeSides.includes("right")
? 90
: 0
}px)`
: "auto",
bottom:
typeof obj.position.bottom === "number"
? `${obj.position.bottom}px`
? `calc(${obj.position.bottom}px + ${
isPlaying && selectedZone.activeSides.includes("bottom")
? 90
: 0
}px)`
: "auto",
}}
onPointerDown={(event) => handlePointerDown(event, index)}
onClick={() => {
setSelectedChartId(obj)
onPointerDown={(event) => {
setSelectedChartId(obj);
handlePointerDown(event, index);
}}
>
{obj.className === "floating total-card" ? (
@ -388,7 +559,7 @@ const DroppedObjects: React.FC = () => {
</>
) : obj.className === "warehouseThroughput floating" ? (
<>
<WarehouseThroughputComponent />
<WarehouseThroughputComponent object={obj}/>
</>
) : obj.className === "fleetEfficiency floating" ? (
<>
@ -432,7 +603,8 @@ const DroppedObjects: React.FC = () => {
))}
{/* Render DistanceLines component during drag */}
{draggingIndex !== null &&
{isPlaying === false &&
draggingIndex !== null &&
activeEdges !== null &&
currentPosition !== null && (
<DistanceLines

View File

@ -25,7 +25,7 @@ interface PanelProps {
lockedPanels: Side[];
zoneId: string;
zoneViewPortTarget: number[];
zoneViewPortPosition: number[]
zoneViewPortPosition: number[];
widgets: Widget[];
};
setSelectedZone: React.Dispatch<
@ -37,7 +37,7 @@ interface PanelProps {
lockedPanels: Side[];
zoneId: string;
zoneViewPortTarget: number[];
zoneViewPortPosition: number[]
zoneViewPortPosition: number[];
widgets: Widget[];
}>
>;
@ -78,8 +78,9 @@ const Panel: React.FC<PanelProps> = ({
case "top":
case "bottom":
return {
width: `calc(100% - ${(leftActive ? panelSize : 0) + (rightActive ? panelSize : 0)
}px)`,
width: `calc(100% - ${
(leftActive ? panelSize : 0) + (rightActive ? panelSize : 0)
}px)`,
height: `${panelSize - 2}px`,
left: leftActive ? `${panelSize}px` : "0",
right: rightActive ? `${panelSize}px` : "0",
@ -89,8 +90,9 @@ const Panel: React.FC<PanelProps> = ({
case "right":
return {
width: `${panelSize - 2}px`,
height: `calc(100% - ${(topActive ? panelSize : 0) + (bottomActive ? panelSize : 0)
}px)`,
height: `calc(100% - ${
(topActive ? panelSize : 0) + (bottomActive ? panelSize : 0)
}px)`,
top: topActive ? `${panelSize}px` : "0",
bottom: bottomActive ? `${panelSize}px` : "0",
[side]: "0",
@ -142,7 +144,7 @@ const Panel: React.FC<PanelProps> = ({
// while dublicate check this and add
const addWidgetToPanel = async (asset: any, panel: Side) => {
const email = localStorage.getItem("email") || "";
const organization = email?.split("@")[1]?.split(".")[0]
const organization = email?.split("@")[1]?.split(".")[0];
const newWidget = {
...asset,
id: generateUniqueId(),
@ -205,7 +207,6 @@ const Panel: React.FC<PanelProps> = ({
const handleReorder = (fromIndex: number, toIndex: number, panel: Side) => {
if (!selectedZone) return; // Ensure selectedZone is not null
setSelectedZone((prev) => {
if (!prev) return prev; // Ensure prev is not null
@ -232,7 +233,9 @@ const Panel: React.FC<PanelProps> = ({
{selectedZone.activeSides.map((side) => (
<div
key={side}
className={`panel ${side}-panel absolute ${isPlaying && ""}`}
className={`panel ${side}-panel absolute ${isPlaying ? "" : ""} ${
hiddenPanels.includes(side) ? "hidePanel" : ""
}`}
style={getPanelStyle(side)}
onDrop={(e) => handleDrop(e, side)}
onDragOver={(e) => e.preventDefault()}

View File

@ -7,7 +7,7 @@ import DisplayZone from "./DisplayZone";
import Scene from "../../../modules/scene/scene";
import useModuleStore from "../../../store/useModuleStore";
import DroppedObjects from "./DroppedFloatingWidgets";
import { useDroppedObjectsStore } from "../../../store/useDroppedObjectsStore";
import {
useAsset3dWidget,
@ -20,6 +20,9 @@ import { generateUniqueId } from "../../../functions/generateUniqueId";
import { determinePosition } from "./functions/determinePosition";
import { addingFloatingWidgets } from "../../../services/realTimeVisulization/zoneData/addFloatingWidgets";
import SocketRealTimeViz from "../../../modules/visualization/realTimeVizSocket.dev";
import RenderOverlay from "../../templates/Overlay";
import ConfirmationPopup from "../../layout/confirmationPopup/ConfirmationPopup";
import DroppedObjects from "./DroppedFloatingWidgets";
type Side = "top" | "bottom" | "left" | "right";
@ -53,6 +56,9 @@ const RealTimeVisulization: React.FC = () => {
const [zonesData, setZonesData] = useState<FormattedZoneData>({});
const { selectedZone, setSelectedZone } = useSelectedZoneStore();
const { zones } = useZones();
const [openConfirmationPopup, setOpenConfirmationPopup] = useState(false);
const [floatingWidgets, setFloatingWidgets] = useState<
Record<string, { zoneName: string; zoneId: string; objects: any[] }>
>({});
@ -66,7 +72,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;
@ -87,9 +93,7 @@ const RealTimeVisulization: React.FC = () => {
{}
);
setZonesData(formattedData);
} catch (error) {
}
} catch (error) {}
}
GetZoneData();
@ -142,7 +146,6 @@ const RealTimeVisulization: React.FC = () => {
id: generateUniqueId(),
position: determinePosition(canvasRect, relativeX, relativeY),
};
console.log('newObject: ', newObject);
// Only set zone if its not already in the store (prevents overwriting objects)
const existingZone =
@ -189,7 +192,7 @@ const RealTimeVisulization: React.FC = () => {
],
},
}));
} catch (error) { }
} catch (error) {}
};
return (
@ -203,6 +206,20 @@ const RealTimeVisulization: React.FC = () => {
left: isPlaying || activeModule !== "visualization" ? "0%" : "",
}}
>
{/* <RenderOverlay>
<EditWidgetOption
options={["Dublicate", "Vertical Move", "Horizontal Move", "Delete"]}
/>
</RenderOverlay> */}
{openConfirmationPopup && (
<RenderOverlay>
<ConfirmationPopup
message={"Are you sure want to delete?"}
onConfirm={() => console.log("confirm")}
onCancel={() => setOpenConfirmationPopup(false)}
/>
</RenderOverlay>
)}
<div
className="scene-container"
style={{

View File

@ -21,7 +21,7 @@ export function determinePosition(
if (relativeY < centerY) {
if (relativeX < centerX) {
console.log("Top-left");
position = {
top: relativeY - 41.5,
left: relativeX - 125,
@ -29,7 +29,7 @@ export function determinePosition(
bottom: "auto",
};
} else {
console.log("Top-right");
position = {
top: relativeY - 41.5,
right: canvasRect.width - relativeX - 125,
@ -39,7 +39,7 @@ export function determinePosition(
}
} else {
if (relativeX < centerX) {
console.log("Bottom-left");
position = {
bottom: canvasRect.height - relativeY - 41.5,
left: relativeX - 125,
@ -47,7 +47,7 @@ export function determinePosition(
top: "auto",
};
} else {
console.log("Bottom-right");
position = {
bottom: canvasRect.height - relativeY - 41.5,
right: canvasRect.width - relativeX - 125,

View File

@ -0,0 +1,20 @@
import { useEffect } from "react";
export const useClickOutside = (
ref: React.RefObject<HTMLElement>,
callback: () => void
) => {
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
if (ref.current && !event.composedPath().includes(ref.current)) {
callback();
}
};
document.addEventListener("mousedown", handleClickOutside);
return () => {
document.removeEventListener("mousedown", handleClickOutside);
};
}, [ref, callback]);
};

View File

@ -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<InputToggleProps> = ({
@ -17,9 +18,10 @@ const InputRange: React.FC<InputToggleProps> = ({
min,
max,
disabled,
value = 5,
value,
onPointerUp,
}) => {
const [rangeValue, setRangeValue] = useState<number>(value);
const [rangeValue, setRangeValue] = useState<number>(value ? value : 5);
function handleChange(e: React.ChangeEvent<HTMLInputElement>) {
const newValue = parseInt(e.target.value); // Parse the value to an integer
@ -31,8 +33,22 @@ const InputRange: React.FC<InputToggleProps> = ({
}
}
useEffect(() => {
setRangeValue(value);
value && setRangeValue(value);
}, [value]);
function handlePointerUp(e: React.PointerEvent<HTMLInputElement>) {
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<HTMLInputElement>) {
const newValue = parseInt(e.currentTarget.value, 10); // Parse value correctly
if (onPointerUp) {
onPointerUp(newValue); // Call the callback function if it exists
}
}
return (
<div className="input-range-container">
@ -52,6 +68,7 @@ const InputRange: React.FC<InputToggleProps> = ({
onChange={handleChange}
disabled={disabled}
value={rangeValue}
onPointerUp={handlePointerUp}
/>
<input
type="number"
@ -61,6 +78,12 @@ const InputRange: React.FC<InputToggleProps> = ({
value={rangeValue}
onChange={handleChange}
disabled={disabled}
onKeyUp={(e) => {
if (e.key === "ArrowUp" || e.key === "ArrowDown") {
console.log("e.key: ", e.key);
handlekey(e);
}
}}
/>
</div>
</div>

View File

@ -0,0 +1,21 @@
import React from "react";
interface EditWidgetOptionProps {
options: string[];
}
const EditWidgetOption: React.FC<EditWidgetOptionProps> = ({ options }) => {
return (
<div className="editWidgetOptions-wrapper">
<div className="editWidgetOptions">
{options.map((option, index) => (
<div className="option" key={index}>
{option}
</div>
))}
</div>
</div>
);
};
export default EditWidgetOption;

View File

@ -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<any>({});
const [duration, setDuration] = useState("1h")
const [name, setName] = useState(title)
const [value, setValue] = useState<any>('')
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(
<div className="chart progressBar">
<div className="header">{name}</div>
<div className="stock">
<span className="stock-item">
<span className="stockValues">
<div className="value">{value}</div>
<div className="key">Units</div>
</span>
<div className="stock-description">{
measurements ? `${measurements?.input1?.fields}` : 'description'}</div>
</span>
<div className="icon">
<StockIncreseIcon />
</div>
</div>
</div>
)
};
export default ProgressCard1;

View File

@ -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<any>({});
const [duration, setDuration] = useState("1h")
const [name, setName] = useState(title)
const [value1, setValue1] = useState<any>('')
const [value2, setValue2] = useState<any>('')
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(
<div className="chart progressBar">
<div className="header">{name}</div>
<div className="stock">
<span className="stock-item">
<span className="stockValues">
<div className="value">{value1}</div>
<div className="key">Units</div>
</span>
<div className="stock-description">{
measurements ? `${measurements?.input1?.fields}` : 'description'}</div>
</span>
<div className="icon">
<StockIncreseIcon />
</div>
</div>
<div className="stock">
<span className="stock-item">
<span className="stockValues">
<div className="value">{value2}</div>
<div className="key">Units</div>
</span>
<div className="stock-description">{
measurements ? `${measurements?.input2?.fields}` : 'description'}</div>
</span>
<div className="icon">
<StockIncreseIcon />
</div>
</div>
</div>
)
};
export default ProgressCard2;

View File

@ -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<HTMLDivElement>) => {
const rect = event.currentTarget.getBoundingClientRect(); // Get position

View File

@ -1,19 +1,99 @@
import React 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";
type Props = {}
const FleetEfficiencyComponent = ({object}: any) => {
const [ progress, setProgress ] = useState<any>(0)
const [measurements, setmeasurements] = useState<any>({});
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 FleetEfficiencyComponent = ({
object
}: any) => {
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 (
<>
<h2 className="header">Fleet Efficiency</h2>
<h2 className="header">{name}</h2>
<div className="progressContainer">
<div className="progress">
<div className="barOverflow">
<div
className="bar"
style={{ transform: `rotate(${object.value}deg)` }}
style={{ transform: `rotate(${rotationAngle}deg)` }}
></div>
</div>
</div>
@ -21,7 +101,7 @@ const FleetEfficiencyComponent = ({
<div className="scaleLabels">
<span>0%</span>
<div className="centerText">
<div className="percentage">{object.per}%</div>
<div className="percentage">{progress}%</div>
<div className="status">Optimal</div>
</div>
<span>100%</span>

View File

@ -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<any>(0)
const [measurements, setmeasurements] = useState<any>({});
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 (
<>
<div className="header-wrapper" onClick={() => {
setSelectedChartId(object.id)
}}>
<div className="header">{object.header}</div>
<div className="header-wrapper" >
<div className="header">{name}</div>
<div className="data-values">
<div className="value">{object.value}</div>
<div className="value">{progress}</div>
<div className="per">{object.per}</div>
</div>
</div>

View File

@ -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<any>({});
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 (
<>
<div className="header">
<h2>Warehouse Throughput</h2>
<p>
<h2>{name}</h2>
{/* <p>
<span>(+5) more</span> in 2025
</p>
</p> */}
</div>
<div className="lineGraph" style={{ height: "100%" }}>
<Line data={lineGraphData} options={lineGraphOptions} />
<Line data={ Object.keys(measurements).length > 0 ? chartData : lineGraphData} options={lineGraphOptions} />
</div>
</>
)

View File

@ -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,10 +145,9 @@ 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);
if (res.type === "Conveyor") {
const pointUUIDs = res.points.map(() => THREE.MathUtils.generateUUID());
@ -177,7 +176,7 @@ async function handleModelLoad(
speed: 'Inherit'
};
console.log('eventData: ', eventData);
// console.log('eventData: ', eventData);
newFloorItem.eventData = eventData;
}
@ -215,6 +214,8 @@ async function handleModelLoad(
eventData: newFloorItem.eventData,
socketId: socket.id
};
console.log('data: ', data);
socket.emit("v2:model-asset:add", data);

View File

@ -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(

View File

@ -1,333 +1,434 @@
import { useFrame, useThree } from "@react-three/fiber";
import { useActiveTool, useAsset3dWidget, useCamMode, useDeletableFloorItem, useDeleteModels, useFloorItems, useLoadingProgress, useRenderDistance, useselectedFloorItem, useSelectedItem, useSocketStore, useToggleView, useTransformMode } from "../../../store/store";
import {
useActiveTool,
useAsset3dWidget,
useCamMode,
useDeletableFloorItem,
useDeleteModels,
useFloorItems,
useLoadingProgress,
useRenderDistance,
useselectedFloorItem,
useSelectedItem,
useSocketStore,
useToggleView,
useTransformMode,
} from "../../../store/store";
import assetVisibility from "../geomentries/assets/assetVisibility";
import { useEffect } from "react";
import * as THREE from "three";
import * as Types from "../../../types/world/worldTypes";
import assetManager, { cancelOngoingTasks } from "../geomentries/assets/assetManager";
import assetManager, {
cancelOngoingTasks,
} from "../geomentries/assets/assetManager";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader";
import DeletableHoveredFloorItems from "../geomentries/assets/deletableHoveredFloorItems";
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));
const gltfLoaderWorker = new Worker(new URL('../../../services/factoryBuilder/webWorkers/gltfLoaderWorker.js', import.meta.url));
const assetManagerWorker = new Worker(
new URL(
"../../../services/factoryBuilder/webWorkers/assetManagerWorker.js",
import.meta.url
)
);
const gltfLoaderWorker = new Worker(
new URL(
"../../../services/factoryBuilder/webWorkers/gltfLoaderWorker.js",
import.meta.url
)
);
const FloorItemsGroup = ({ itemsGroup, hoveredDeletableFloorItem, AttachedObject, floorGroup, tempLoader, isTempLoader, plane }: any) => {
const state: Types.ThreeState = useThree();
const { raycaster, controls }: any = state;
const { renderDistance } = useRenderDistance();
const { toggleView } = useToggleView();
const { floorItems, setFloorItems } = useFloorItems();
const { camMode } = useCamMode();
const { deleteModels } = useDeleteModels();
const { setDeletableFloorItem } = useDeletableFloorItem();
const { transformMode } = useTransformMode();
const { setselectedFloorItem } = useselectedFloorItem();
const { activeTool } = useActiveTool();
const { selectedItem, setSelectedItem } = useSelectedItem();
const { setLoadingProgress } = useLoadingProgress();
const { activeModule } = useModuleStore();
const { socket } = useSocketStore();
const loader = new GLTFLoader();
const dracoLoader = new DRACOLoader();
const FloorItemsGroup = ({
itemsGroup,
hoveredDeletableFloorItem,
AttachedObject,
floorGroup,
tempLoader,
isTempLoader,
plane,
}: any) => {
const state: Types.ThreeState = useThree();
const { raycaster, controls }: any = state;
const { renderDistance } = useRenderDistance();
const { toggleView } = useToggleView();
const { floorItems, setFloorItems } = useFloorItems();
const { camMode } = useCamMode();
const { deleteModels } = useDeleteModels();
const { setDeletableFloorItem } = useDeletableFloorItem();
const { transformMode } = useTransformMode();
const { setselectedFloorItem } = useselectedFloorItem();
const { activeTool } = useActiveTool();
const { selectedItem, setSelectedItem } = useSelectedItem();
const { setLoadingProgress } = useLoadingProgress();
const { activeModule } = useModuleStore();
const { socket } = useSocketStore();
const loader = new GLTFLoader();
const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath('https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/libs/draco/gltf/');
loader.setDRACOLoader(dracoLoader);
dracoLoader.setDecoderPath(
"https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/libs/draco/gltf/"
);
loader.setDRACOLoader(dracoLoader);
useEffect(() => {
const email = localStorage.getItem('email');
const organization = (email!.split("@")[1]).split(".")[0];
useEffect(() => {
const email = localStorage.getItem("email");
const organization = email!.split("@")[1].split(".")[0];
let totalAssets = 0;
let loadedAssets = 0;
let totalAssets = 0;
let loadedAssets = 0;
const updateLoadingProgress = (progress: number) => {
if (progress < 100) {
setLoadingProgress(progress);
} else if (progress === 100) {
setTimeout(() => {
setLoadingProgress(100);
setTimeout(() => {
setLoadingProgress(0);
}, 1500);
}, 1000);
}
};
const updateLoadingProgress = (progress: number) => {
if (progress < 100) {
setLoadingProgress(progress);
} else if (progress === 100) {
setTimeout(() => {
setLoadingProgress(100);
setTimeout(() => {
setLoadingProgress(0);
}, 1500);
}, 1000);
}
};
getFloorItems(organization).then((data) => {
console.log('data: ', data);
const uniqueItems = (data as Types.FloorItems).filter((item, index, self) =>
index === self.findIndex((t) => t.modelfileID === item.modelfileID)
);
totalAssets = uniqueItems.length;
if (totalAssets === 0) {
updateLoadingProgress(100);
return;
}
gltfLoaderWorker.postMessage({ floorItems: data });
getFloorAssets(organization).then((data) => {
const uniqueItems = (data as Types.FloorItems).filter(
(item, index, self) =>
index === self.findIndex((t) => t.modelfileID === item.modelfileID)
);
totalAssets = uniqueItems.length;
if (totalAssets === 0) {
updateLoadingProgress(100);
return;
}
gltfLoaderWorker.postMessage({ floorItems: data });
});
gltfLoaderWorker.onmessage = async (event) => {
if (event.data.message === "gltfLoaded" && event.data.modelBlob) {
const blobUrl = URL.createObjectURL(event.data.modelBlob);
loader.load(blobUrl, (gltf) => {
URL.revokeObjectURL(blobUrl);
THREE.Cache.remove(blobUrl);
THREE.Cache.add(event.data.modelID, gltf);
loadedAssets++;
const progress = Math.round((loadedAssets / totalAssets) * 100);
updateLoadingProgress(progress);
if (loadedAssets === totalAssets) {
loadInitialFloorItems(itemsGroup, setFloorItems);
updateLoadingProgress(100);
}
});
}
};
}, []);
gltfLoaderWorker.onmessage = async (event) => {
if (event.data.message === "gltfLoaded" && event.data.modelBlob) {
const blobUrl = URL.createObjectURL(event.data.modelBlob);
useEffect(() => {
assetManagerWorker.onmessage = async (event) => {
cancelOngoingTasks(); // Cancel the ongoing process
await assetManager(event.data, itemsGroup, loader);
};
}, [assetManagerWorker]);
loader.load(blobUrl, (gltf) => {
URL.revokeObjectURL(blobUrl);
THREE.Cache.remove(blobUrl);
THREE.Cache.add(event.data.modelID, gltf);
useEffect(() => {
if (toggleView) return;
loadedAssets++;
const progress = Math.round((loadedAssets / totalAssets) * 100);
updateLoadingProgress(progress);
const uuids: string[] = [];
itemsGroup.current?.children.forEach((child: any) => {
uuids.push(child.uuid);
});
const cameraPosition = state.camera.position;
if (loadedAssets === totalAssets) {
loadInitialFloorItems(itemsGroup, setFloorItems);
updateLoadingProgress(100);
}
});
}
};
}, []);
assetManagerWorker.postMessage({
floorItems,
cameraPosition,
uuids,
renderDistance,
});
}, [camMode, renderDistance]);
useEffect(() => {
assetManagerWorker.onmessage = async (event) => {
cancelOngoingTasks(); // Cancel the ongoing process
await assetManager(event.data, itemsGroup, loader);
};
}, [assetManagerWorker]);
useEffect(() => {
const controls: any = state.controls;
const camera: any = state.camera;
useEffect(() => {
if (toggleView) return
if (controls) {
let intervalId: NodeJS.Timeout | null = null;
const handleChange = () => {
if (toggleView) return;
const uuids: string[] = [];
itemsGroup.current?.children.forEach((child: any) => {
uuids.push(child.uuid);
uuids.push(child.uuid);
});
const cameraPosition = state.camera.position;
const cameraPosition = camera.position;
assetManagerWorker.postMessage({ floorItems, cameraPosition, uuids, renderDistance });
}, [camMode, renderDistance]);
assetManagerWorker.postMessage({
floorItems,
cameraPosition,
uuids,
renderDistance,
});
};
useEffect(() => {
const controls: any = state.controls;
const camera: any = state.camera;
if (controls) {
let intervalId: NodeJS.Timeout | null = null;
const handleChange = () => {
if (toggleView) return
const uuids: string[] = [];
itemsGroup.current?.children.forEach((child: any) => {
uuids.push(child.uuid);
});
const cameraPosition = camera.position;
assetManagerWorker.postMessage({ floorItems, cameraPosition, uuids, renderDistance });
};
const startInterval = () => {
if (!intervalId) {
intervalId = setInterval(handleChange, 50);
}
};
const stopInterval = () => {
handleChange();
if (intervalId) {
clearInterval(intervalId);
intervalId = null;
}
};
controls.addEventListener('rest', handleChange);
controls.addEventListener('rest', stopInterval);
controls.addEventListener('control', startInterval);
controls.addEventListener('controlend', stopInterval);
return () => {
controls.removeEventListener('rest', handleChange);
controls.removeEventListener('rest', stopInterval);
controls.removeEventListener('control', startInterval);
controls.removeEventListener('controlend', stopInterval);
if (intervalId) {
clearInterval(intervalId);
}
};
const startInterval = () => {
if (!intervalId) {
intervalId = setInterval(handleChange, 50);
}
}, [state.controls, floorItems, toggleView, renderDistance]);
};
useEffect(() => {
const canvasElement = state.gl.domElement;
let drag = false;
let isLeftMouseDown = false;
const onMouseDown = (evt: any) => {
if (evt.button === 0) {
isLeftMouseDown = true;
drag = false;
}
};
const onMouseMove = () => {
if (isLeftMouseDown) {
drag = true;
}
};
const onMouseUp = async (evt: any) => {
if (controls) {
(controls as any).enabled = true;
}
if (evt.button === 0) {
isLeftMouseDown = false;
if (drag) return;
if (deleteModels) {
DeleteFloorItems(itemsGroup, hoveredDeletableFloorItem, setFloorItems, socket);
}
const Mode = transformMode;
if (Mode !== null || activeTool === "cursor") {
if (!itemsGroup.current) return;
let intersects = raycaster.intersectObjects(itemsGroup.current.children, true);
if (intersects.length > 0 && intersects[0]?.object?.parent?.parent?.position && intersects[0]?.object?.parent?.parent?.scale && intersects[0]?.object?.parent?.parent?.rotation) {
// let currentObject = intersects[0].object;
// while (currentObject) {
// if (currentObject.name === "Scene") {
// break;
// }
// currentObject = currentObject.parent as THREE.Object3D;
// }
// if (currentObject) {
// AttachedObject.current = currentObject as any;
// setselectedFloorItem(AttachedObject.current!);
// }
} else {
const target = controls.getTarget(new THREE.Vector3());
await controls.setTarget(target.x, 0, target.z, true);
setselectedFloorItem(null);
}
}
}
};
const onDblClick = async (evt: any) => {
if (evt.button === 0) {
isLeftMouseDown = false;
if (drag) return;
const Mode = transformMode;
if (Mode !== null || activeTool === "cursor") {
if (!itemsGroup.current) return;
let intersects = raycaster.intersectObjects(itemsGroup.current.children, true);
if (intersects.length > 0 && intersects[0]?.object?.parent?.parent?.position && intersects[0]?.object?.parent?.parent?.scale && intersects[0]?.object?.parent?.parent?.rotation) {
let currentObject = intersects[0].object;
while (currentObject) {
if (currentObject.name === "Scene") {
break;
}
currentObject = currentObject.parent as THREE.Object3D;
}
if (currentObject) {
AttachedObject.current = currentObject as any;
// controls.fitToSphere(AttachedObject.current!, true);
const bbox = new THREE.Box3().setFromObject(AttachedObject.current);
const size = bbox.getSize(new THREE.Vector3());
const center = bbox.getCenter(new THREE.Vector3());
const front = new THREE.Vector3(0, 0, 1);
AttachedObject.current.localToWorld(front);
front.sub(AttachedObject.current.position).normalize();
const distance = Math.max(size.x, size.y, size.z) * 2;
const newPosition = center.clone().addScaledVector(front, distance);
controls.setPosition(newPosition.x, newPosition.y, newPosition.z, true);
controls.setTarget(center.x, center.y, center.z, true);
controls.fitToBox(AttachedObject.current!, true, { cover: true, paddingTop: 5, paddingLeft: 5, paddingBottom: 5, paddingRight: 5 });
setselectedFloorItem(AttachedObject.current!);
}
} else {
const target = controls.getTarget(new THREE.Vector3());
await controls.setTarget(target.x, 0, target.z, true);
setselectedFloorItem(null);
}
}
}
const stopInterval = () => {
handleChange();
if (intervalId) {
clearInterval(intervalId);
intervalId = null;
}
};
const onDrop = (event: any) => {
controls.addEventListener("rest", handleChange);
controls.addEventListener("rest", stopInterval);
controls.addEventListener("control", startInterval);
controls.addEventListener("controlend", stopInterval);
if (!event.dataTransfer?.files[0]) return;
if (selectedItem.id !== "" && event.dataTransfer?.files[0]) {
addAssetModel(raycaster, state.camera, state.pointer, floorGroup, setFloorItems, itemsGroup, isTempLoader, tempLoader, socket, selectedItem, setSelectedItem, plane);
}
return () => {
controls.removeEventListener("rest", handleChange);
controls.removeEventListener("rest", stopInterval);
controls.removeEventListener("control", startInterval);
controls.removeEventListener("controlend", stopInterval);
if (intervalId) {
clearInterval(intervalId);
}
};
}
}, [state.controls, floorItems, toggleView, renderDistance]);
const onDragOver = (event: any) => {
event.preventDefault();
};
useEffect(() => {
const canvasElement = state.gl.domElement;
let drag = false;
let isLeftMouseDown = false;
if (activeModule === "builder") {
canvasElement.addEventListener("mousedown", onMouseDown);
canvasElement.addEventListener("mouseup", onMouseUp);
canvasElement.addEventListener("mousemove", onMouseMove);
canvasElement.addEventListener("dblclick", onDblClick);
canvasElement.addEventListener("drop", onDrop);
canvasElement.addEventListener("dragover", onDragOver);
} else {
if (controls) {
const target = controls.getTarget(new THREE.Vector3());
controls.setTarget(target.x, 0, target.z, true);
setselectedFloorItem(null);
}
}
const onMouseDown = (evt: any) => {
if (evt.button === 0) {
isLeftMouseDown = true;
drag = false;
}
};
return () => {
canvasElement.removeEventListener("mousedown", onMouseDown);
canvasElement.removeEventListener("mouseup", onMouseUp);
canvasElement.removeEventListener("mousemove", onMouseMove);
canvasElement.removeEventListener("dblclick", onDblClick);
canvasElement.removeEventListener("drop", onDrop);
canvasElement.removeEventListener("dragover", onDragOver);
};
}, [deleteModels, transformMode, controls, selectedItem, state.camera, state.pointer, activeTool, activeModule]);
const onMouseMove = () => {
if (isLeftMouseDown) {
drag = true;
}
};
useEffect(() => {
const onMouseUp = async (evt: any) => {
if (controls) {
(controls as any).enabled = true;
}
if (evt.button === 0) {
isLeftMouseDown = false;
if (drag) return;
console.log('floorItems: ', floorItems);
}, [floorItems])
useFrame(() => {
if (controls)
assetVisibility(itemsGroup, state.camera.position, renderDistance);
if (deleteModels) {
DeletableHoveredFloorItems(state, itemsGroup, hoveredDeletableFloorItem, setDeletableFloorItem);
} else if (!deleteModels) {
if (hoveredDeletableFloorItem.current) {
hoveredDeletableFloorItem.current = undefined;
setDeletableFloorItem(null);
}
DeleteFloorItems(
itemsGroup,
hoveredDeletableFloorItem,
setFloorItems,
socket
);
}
})
const Mode = transformMode;
return (
<group ref={itemsGroup} name="itemsGroup">
</group>
)
}
if (Mode !== null || activeTool === "cursor") {
if (!itemsGroup.current) return;
let intersects = raycaster.intersectObjects(
itemsGroup.current.children,
true
);
if (
intersects.length > 0 &&
intersects[0]?.object?.parent?.parent?.position &&
intersects[0]?.object?.parent?.parent?.scale &&
intersects[0]?.object?.parent?.parent?.rotation
) {
// let currentObject = intersects[0].object;
// while (currentObject) {
// if (currentObject.name === "Scene") {
// break;
// }
// currentObject = currentObject.parent as THREE.Object3D;
// }
// if (currentObject) {
// AttachedObject.current = currentObject as any;
// setselectedFloorItem(AttachedObject.current!);
// }
} else {
const target = controls.getTarget(new THREE.Vector3());
await controls.setTarget(target.x, 0, target.z, true);
setselectedFloorItem(null);
}
}
}
};
export default FloorItemsGroup;
const onDblClick = async (evt: any) => {
if (evt.button === 0) {
isLeftMouseDown = false;
if (drag) return;
const Mode = transformMode;
if (Mode !== null || activeTool === "cursor") {
if (!itemsGroup.current) return;
let intersects = raycaster.intersectObjects(
itemsGroup.current.children,
true
);
if (
intersects.length > 0 &&
intersects[0]?.object?.parent?.parent?.position &&
intersects[0]?.object?.parent?.parent?.scale &&
intersects[0]?.object?.parent?.parent?.rotation
) {
let currentObject = intersects[0].object;
while (currentObject) {
if (currentObject.name === "Scene") {
break;
}
currentObject = currentObject.parent as THREE.Object3D;
}
if (currentObject) {
AttachedObject.current = currentObject as any;
// controls.fitToSphere(AttachedObject.current!, true);
const bbox = new THREE.Box3().setFromObject(
AttachedObject.current
);
const size = bbox.getSize(new THREE.Vector3());
const center = bbox.getCenter(new THREE.Vector3());
const front = new THREE.Vector3(0, 0, 1);
AttachedObject.current.localToWorld(front);
front.sub(AttachedObject.current.position).normalize();
const distance = Math.max(size.x, size.y, size.z) * 2;
const newPosition = center
.clone()
.addScaledVector(front, distance);
controls.setPosition(
newPosition.x,
newPosition.y,
newPosition.z,
true
);
controls.setTarget(center.x, center.y, center.z, true);
controls.fitToBox(AttachedObject.current!, true, {
cover: true,
paddingTop: 5,
paddingLeft: 5,
paddingBottom: 5,
paddingRight: 5,
});
setselectedFloorItem(AttachedObject.current!);
}
} else {
const target = controls.getTarget(new THREE.Vector3());
await controls.setTarget(target.x, 0, target.z, true);
setselectedFloorItem(null);
}
}
}
};
const onDrop = (event: any) => {
if (!event.dataTransfer?.files[0]) return;
if (selectedItem.id !== "" && event.dataTransfer?.files[0]) {
addAssetModel(
raycaster,
state.camera,
state.pointer,
floorGroup,
setFloorItems,
itemsGroup,
isTempLoader,
tempLoader,
socket,
selectedItem,
setSelectedItem,
plane
);
}
};
const onDragOver = (event: any) => {
event.preventDefault();
};
if (activeModule === "builder") {
canvasElement.addEventListener("mousedown", onMouseDown);
canvasElement.addEventListener("mouseup", onMouseUp);
canvasElement.addEventListener("mousemove", onMouseMove);
canvasElement.addEventListener("dblclick", onDblClick);
canvasElement.addEventListener("drop", onDrop);
canvasElement.addEventListener("dragover", onDragOver);
} else {
if (controls) {
const target = controls.getTarget(new THREE.Vector3());
controls.setTarget(target.x, 0, target.z, true);
setselectedFloorItem(null);
}
}
return () => {
canvasElement.removeEventListener("mousedown", onMouseDown);
canvasElement.removeEventListener("mouseup", onMouseUp);
canvasElement.removeEventListener("mousemove", onMouseMove);
canvasElement.removeEventListener("dblclick", onDblClick);
canvasElement.removeEventListener("drop", onDrop);
canvasElement.removeEventListener("dragover", onDragOver);
};
}, [
deleteModels,
transformMode,
controls,
selectedItem,
state.camera,
state.pointer,
activeTool,
activeModule,
]);
useEffect(() => {}, [floorItems]);
useFrame(() => {
if (controls)
assetVisibility(itemsGroup, state.camera.position, renderDistance);
if (deleteModels) {
DeletableHoveredFloorItems(
state,
itemsGroup,
hoveredDeletableFloorItem,
setDeletableFloorItem
);
} else if (!deleteModels) {
if (hoveredDeletableFloorItem.current) {
hoveredDeletableFloorItem.current = undefined;
setDeletableFloorItem(null);
}
}
});
return <group ref={itemsGroup} name="itemsGroup"></group>;
};
export default FloorItemsGroup;

View File

@ -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) => {

View File

@ -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])

View File

@ -57,7 +57,6 @@ const FilterSearch: React.FC<ModelsProps> = ({
const filteredModel = filteredModels?.filter((model) =>
model.filename.toLowerCase().includes(val.toLowerCase())
);
setModels(filteredModel);
};

View File

@ -25,7 +25,6 @@ const MarketPlace = () => {
const filteredAssets = async () => {
try {
const filt = await getAssetImages("67d934ad0f42a1fdadb19aa6");
setModels(filt.items);
setFilteredModels(filt.items);
} catch {}

View File

@ -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,7 @@ 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);
localStorage.setItem("FloorItems", JSON.stringify(items));
await initializeDB();
@ -121,18 +121,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 +185,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' });

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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) {

View File

@ -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;

View File

@ -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 (
<mesh name="Ground">
<mesh ref={grid} name="Grid" position={!toggleView ? CONSTANTS.gridConfig.position3D : CONSTANTS.gridConfig.position2D}>
<gridHelper args={[CONSTANTS.gridConfig.size, CONSTANTS.gridConfig.divisions, CONSTANTS.gridConfig.primaryColor, CONSTANTS.gridConfig.secondaryColor]} />
</mesh>
<mesh ref={plane} rotation-x={CONSTANTS.planeConfig.rotation} position={!toggleView ? CONSTANTS.planeConfig.position3D : CONSTANTS.planeConfig.position2D} name="Plane" receiveShadow>
<planeGeometry args={[CONSTANTS.planeConfig.width, CONSTANTS.planeConfig.height]} />
<meshBasicMaterial color={CONSTANTS.planeConfig.color} />
</mesh>
</mesh>
)
}
return (
<mesh name="Ground">
<mesh
ref={grid}
name="Grid"
position={
!toggleView
? CONSTANTS.gridConfig.position3D
: CONSTANTS.gridConfig.position2D
}
>
<gridHelper
args={[
gridValue.size,
gridValue.divisions,
// CONSTANTS.gridConfig.size,
// CONSTANTS.gridConfig.divisions,
CONSTANTS.gridConfig.primaryColor,
CONSTANTS.gridConfig.secondaryColor,
]}
/>
</mesh>
<mesh
ref={plane}
rotation-x={CONSTANTS.planeConfig.rotation}
position={
!toggleView
? CONSTANTS.planeConfig.position3D
: CONSTANTS.planeConfig.position2D
}
name="Plane"
receiveShadow
>
<planeGeometry
args={[planeValue.width, planeValue.height]}
// args={[CONSTANTS.planeConfig.width, CONSTANTS.planeConfig.height]}
/>
<meshBasicMaterial color={CONSTANTS.planeConfig.color} />
</mesh>
</mesh>
);
};
export default Ground;

View File

@ -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}
/>
<object3D ref={targetRef} />
<mesh position={CONSTANTS.shadowConfig.shadowMaterialPosition} rotation={CONSTANTS.shadowConfig.shadowMaterialRotation} receiveShadow>
<planeGeometry args={[CONSTANTS.planeConfig.width, CONSTANTS.planeConfig.height]} />
<shadowMaterial opacity={CONSTANTS.shadowConfig.shadowMaterialOpacity} transparent />
<mesh
position={CONSTANTS.shadowConfig.shadowMaterialPosition}
rotation={CONSTANTS.shadowConfig.shadowMaterialRotation}
receiveShadow
>
{/* <planeGeometry
args={[CONSTANTS.planeConfig.width, CONSTANTS.planeConfig.height]}
/>
<shadowMaterial
opacity={CONSTANTS.shadowConfig.shadowMaterialOpacity}
transparent
/> */}
<planeGeometry args={[planeValue.width, planeValue.height]} />
<shadowMaterial
opacity={CONSTANTS.shadowConfig.shadowMaterialOpacity}
transparent
/>
</mesh>
</>
);
}
}

View File

@ -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();

View File

@ -12,14 +12,15 @@ function Behaviour() {
floorItems.forEach((item: Types.FloorItemType) => {
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,
@ -29,7 +30,7 @@ function Behaviour() {
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 }],
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: [] },
},
@ -37,7 +38,7 @@ function Behaviour() {
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 }],
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: [] },
},
@ -45,13 +46,13 @@ function Behaviour() {
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 }],
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: [] },
},
],
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 +72,7 @@ function Behaviour() {
connections: { source: { pathUUID: item.modeluuid, pointUUID: pointUUID }, targets: [] },
speed: 2,
},
assetPosition: [...item.position],
position: [...item.position],
};
newPaths.push(newVehiclePath);

View File

@ -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();

View File

@ -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<THREE.Group>(null);
const meshRef = useRef<THREE.Mesh>(null);
const [visible, setVisible] = useState(false);
const spawnedObjectsRef = useRef<THREE.Object3D[]>([]);
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<THREE.Material>(
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<AnimationState>({
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 ? (
<mesh ref={meshRef} material={currentMaterial}>
<boxGeometry args={[1, 1, 1]} />
</mesh>
) : null;
}
return visible ? (
<group ref={groupRef}>
<primitive
object={gltf.scene.clone()}
scale={[1, 1, 1]}
material={currentMaterial}
/>
</group>
) : null;
};
export default ProcessAnimator;

View File

@ -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<any[]>([]);
return (
<>
<ProcessCreator onProcessesCreated={setProcesses} />
{processes.length > 0 && <ProcessAnimator processes={processes} />}
</>
);
};
export default ProcessContainer;

View File

@ -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<Process[]>([]);
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<string> = new Set()
): SimulationPath[] => {
const connectedPaths: SimulationPath[] = [];
const queue: SimulationPath[] = [initialPath];
visited.add(initialPath.modeluuid);
const pathMap = new Map<string, SimulationPath>();
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<string>();
const processes: Process[] = [];
const pathMap = new Map<string, SimulationPath>();
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<ProcessCreatorProps> = React.memo(
({ onProcessesCreated }) => {
const { simulationPaths } = useSimulationPaths();
const { createProcessesFromPaths } = useProcessCreation();
const prevPathsRef = useRef<SimulationPath[]>([]);
const prevProcessesRef = useRef<Process[]>([]);
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;

View File

@ -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();
@ -13,6 +14,7 @@ function Simulation() {
const [processes, setProcesses] = useState([]);
useEffect(() => {
// console.log('simulationPaths: ', simulationPaths);
}, [simulationPaths]);
// useEffect(() => {
@ -35,6 +37,7 @@ function Simulation() {
<>
<PathCreation pathsGroupRef={pathsGroupRef} />
<PathConnector pathsGroupRef={pathsGroupRef} />
<ProcessContainer />
</>
)}
</>

View File

@ -1,4 +1,4 @@
import React, { useEffect } from "react";
import React, { useEffect, useState } from "react";
import ModuleToggle from "../components/ui/ModuleToggle";
import SideBarLeft from "../components/layout/sidebarLeft/SideBarLeft";
import SideBarRight from "../components/layout/sidebarRight/SideBarRight";
@ -20,6 +20,8 @@ import { usePlayButtonStore } from "../store/usePlayButtonStore";
import MarketPlace from "../modules/market/MarketPlace";
import LoadingPage from "../components/templates/LoadingPage";
import SimulationPlayer from "../components/ui/simulation/simulationPlayer";
import RenderOverlay from "../components/templates/Overlay";
import MenuBar from "../components/ui/menu/menu";
const Project: React.FC = () => {
let navigate = useNavigate();
@ -30,6 +32,7 @@ const Project: React.FC = () => {
const { setFloorItems } = useFloorItems();
const { setWallItems } = useWallItems();
const { setZones } = useZones();
const [openMenu, setOpenMenu] = useState<boolean>(true);
useEffect(() => {
setFloorItems([]);
@ -61,6 +64,9 @@ const Project: React.FC = () => {
<SideBarRight />
</>
)}
{/* <RenderOverlay>
<MenuBar setOpenMenu={setOpenMenu} />
</RenderOverlay> */}
{activeModule === "market" && <MarketPlace />}
<RealTimeVisulization />
{activeModule !== "market" && <Tools />}

View File

@ -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(

View File

@ -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();

View File

@ -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,

View File

@ -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");
}
};
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");
}
}
};

View File

@ -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");
}
};
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");
}
}
};

View File

@ -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);

View File

@ -6,9 +6,9 @@ export const addingFloatingWidgets = async (
organization: string,
widget: {}
) => {
console.log('organization: ', organization);
console.log('widget: ', widget);
console.log('zoneId: ', zoneId);
try {
const response = await fetch(
`${url_Backend_dwinzo}/api/v2/floatwidget/save`,

View File

@ -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 {

View File

@ -264,7 +264,7 @@ export const useAzimuth = create<any>((set: any) => ({
}));
export const useRenderDistance = create<any>((set: any) => ({
renderDistance: 50,
renderDistance: 40,
setRenderDistance: (x: any) => set({ renderDistance: x }),
}));
@ -409,3 +409,22 @@ export const useWidgetSubOption = create<any>((set: any) => ({
widgetSubOption: "2D",
setWidgetSubOption: (x: any) => set({ widgetSubOption: x }),
}));
export const useLimitDistance = create<any>((set: any) => ({
limitDistance: true,
setLimitDistance: (x: any) => set({ limitDistance: x }),
}));
export const useTileDistance = create<any>((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 },
})),
}));

View File

@ -10,9 +10,15 @@ interface MeasurementStore {
interval: number;
duration: string;
name: string;
header: string;
flotingDuration: string;
flotingMeasurements: Record<string, Measurement>; // Change array to Record<string, Measurement>
setMeasurements: (newMeasurements: Record<string, Measurement>) => void;
updateDuration: (newDuration: string) => void;
updateName: (newName: string) => void;
updateHeader: (newHeader: string) => void;
updateFlotingDuration: (newFlotingDuration: string) => void;
setFlotingMeasurements: (newFlotingMeasurements: Record<string, Measurement>) => void;
}
const useChartStore = create<MeasurementStore>((set) => ({
@ -20,6 +26,9 @@ const useChartStore = create<MeasurementStore>((set) => ({
interval: 1000,
duration: "1h",
name:'',
header:'',
flotingDuration: "1h",
flotingMeasurements: {},
setMeasurements: (newMeasurements) =>
set(() => ({ measurements: newMeasurements })),
@ -28,7 +37,16 @@ const useChartStore = create<MeasurementStore>((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;

View File

@ -0,0 +1,43 @@
.confirmation-overlay {
width: 100vw;
height: 100vh;
background: var(--background-color-secondary);
backdrop-filter: blur(2px);
.confirmation-modal {
min-width: 35%;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 5;
background-color: var(--background-color);
padding: 14px 12px;
border-radius: 6px;
.buttton-wrapper {
padding-top: 12px;
display: flex;
justify-content: end;
align-items: end;
gap: 12px;
.confirmation-button {
padding: 6px 10px;
border-radius: 6px;
cursor: pointer;
&:first-child {
color: var(--accent-color);
}
&:last-child {
background-color: #ffe3e0;
color: #f65648;
}
}
}
}
}

View File

@ -551,7 +551,7 @@ input {
}
.input-value {
width: 40px;
width: 42px;
text-align: center;
&::-webkit-inner-spin-button,
&::-webkit-outer-spin-button {

View File

@ -113,7 +113,9 @@
flex-direction: column;
justify-content: center;
gap: 6px;
.assets-container {
height: auto;
}
.icon {
position: absolute;
top: 12px;

View File

@ -253,7 +253,7 @@
.user-profile-container {
display: flex;
.user-profile{
.user-profile {
background: var(--accent-color);
color: var(--primary-color);
}
@ -320,9 +320,7 @@
.dataSideBar {
.inputs-wrapper {
.datas {
.input-value {
padding: 5px 10px;
}
@ -688,7 +686,7 @@
font-weight: var(--font-weight-regular);
padding: 8px 0;
}
.input-toggle-container{
.input-toggle-container {
padding: 0;
margin-bottom: 6px;
}
@ -963,6 +961,7 @@
padding: 0 6px;
.assets-wrapper {
width: 100%;
position: relative;
margin: 8px 10px;
@ -1010,9 +1009,11 @@
top: 50%;
right: -10px;
transform: translate(0, -50%);
background: linear-gradient(144.19deg,
#f1e7cd 16.62%,
#fffaef 85.81%);
background: linear-gradient(
144.19deg,
#f1e7cd 16.62%,
#fffaef 85.81%
);
}
.category-image {
@ -1075,4 +1076,52 @@
cursor: pointer;
}
}
}
}
.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;
}
}

View File

@ -24,6 +24,7 @@
@use 'components/marketPlace/marketPlace';
@use 'components/simulation/simulation';
@use 'components/menu/menu';
@use 'components/confirmationPopUp';
// layout
@use 'layout/loading';

View File

@ -26,7 +26,7 @@
border-radius: $border-radius-medium;
padding: 18px;
position: absolute;
z-index: 1000;
z-index: 1;
}
.scene-container {
@ -172,6 +172,7 @@
border-radius: 6px;
overflow: visible !important;
z-index: $z-index-tools;
overflow: auto;
.panel-content {
position: relative;
@ -320,6 +321,10 @@
bottom: 0;
}
}
.panel.hidePanel {
opacity: 0;
}
}
.playingFlase {
@ -545,20 +550,21 @@
.floating-wrapper {
.icon {
width: 25px !important;
height: 25px !important;
background-color: transparent;
// width: 25px !important;
// height: 25px !important;
// background-color: transparent;
}
.kebab {
width: 30px;
height: 30px;
width: 25px;
height: 25px;
position: absolute !important;
top: 0px;
right: 0px;
z-index: 10;
cursor: pointer;
@include flex-center;
background-color: transparent !important;
}
.kebab-options {
@ -576,6 +582,12 @@
box-shadow: var(--box-shadow-medium);
.icon {
width: 25px !important;
height: 25px !important;
background-color: transparent;
}
.btn {
display: flex;
gap: 6px;
@ -607,18 +619,19 @@
}
}
.dublicate {
cursor: not-allowed;
}
}
}
.distance-line {
position: absolute;
border-style: dashed;
border-color: var(--accent-color); /* Green color for visibility */
border-color: var(--accent-color);
/* Green color for visibility */
border-width: 1px;
pointer-events: none; /* Ensure lins don't interfere with dragging */
pointer-events: none;
/* Ensure lins don't interfere with dragging */
z-index: 10000;
}
@ -631,51 +644,112 @@
padding: 2px 6px;
border-radius: 3px;
white-space: nowrap;
transform: translate(-50%, -50%); /* Center the label */
transform: translate(-50%, -50%);
/* Center the label */
}
/* Specific styles for each type of line */
/* Top distance line */
.distance-line.top {
border-bottom: none; /* Remove bottom border for a single line */
width: 2px; /* Thin vertical line */
border-bottom: none;
/* Remove bottom border for a single line */
width: 2px;
/* Thin vertical line */
}
.distance-line.top .distance-label {
top: -10px; /* Position label above the line */
left: 50%; /* Center horizontally */
top: -10px;
/* Position label above the line */
left: 50%;
/* Center horizontally */
}
/* Bottom distance line */
.distance-line.bottom {
border-top: none; /* Remove top border for a single line */
width: 2px; /* Thin vertical line */
border-top: none;
/* Remove top border for a single line */
width: 2px;
/* Thin vertical line */
}
.distance-line.bottom .distance-label {
bottom: -10px; /* Position label below the line */
left: 50%; /* Center horizontally */
bottom: -10px;
/* Position label below the line */
left: 50%;
/* Center horizontally */
}
/* Left distance line */
.distance-line.left {
border-right: none; /* Remove right border for a single line */
height: 2px; /* Thin horizontal line */
border-right: none;
/* Remove right border for a single line */
height: 2px;
/* Thin horizontal line */
}
.distance-line.left .distance-label {
left: -10px; /* Position label to the left of the line */
top: 50%; /* Center vertically */
left: -10px;
/* Position label to the left of the line */
top: 50%;
/* Center vertically */
}
/* Right distance line */
.distance-line.right {
border-left: none; /* Remove left border for a single line */
height: 2px; /* Thin horizontal line */
border-left: none;
/* Remove left border for a single line */
height: 2px;
/* Thin horizontal line */
}
.distance-line.right .distance-label {
right: -10px; /* Position label to the right of the line */
top: 50%; /* Center vertically */
right: -10px;
/* Position label to the right of the line */
top: 50%;
/* Center vertically */
}
.activeChart {
outline: 1px solid var(--accent-color);
z-index: 2 !important;
}
.editWidgetOptions-wrapper {
height: 100vh;
width: 100vw;
}
.editWidgetOptions {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background-color: var(--background-color);
z-index: 3;
display: flex;
flex-direction: column;
border-radius: 6px;
overflow: hidden;
.option {
padding: 8px 10px;
color: var(--text-color);
cursor: pointer;
&:hover {
background-color: var(--highlight-accent-color);
color: var(--accent-color);
}
&:last-child {
color: #f65648;
&:hover {
background-color: #ffe3e0;
}
}
}
}

View File

@ -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

View File

@ -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];
}

View File

@ -17,3 +17,5 @@ export function toggleTheme() {
document.documentElement.setAttribute('data-theme', newTheme);
localStorage.setItem('theme', newTheme);
}
setTheme();