Merge branch 'main' into simulation-animation

This commit is contained in:
SreeNath14 2025-04-03 10:29:40 +05:30
commit 1406efd808
75 changed files with 5417 additions and 1499 deletions

View File

@ -1,5 +1,5 @@
import { Html } from "@react-three/drei"; import { Html } from "@react-three/drei";
import React from "react"; import React, { useEffect, useMemo, useState } from "react";
import { Bar } from "react-chartjs-2"; import { Bar } from "react-chartjs-2";
import { import {
Chart as ChartJS, Chart as ChartJS,
@ -11,6 +11,11 @@ import {
Legend, Legend,
TooltipItem, // Import TooltipItem for typing TooltipItem, // Import TooltipItem for typing
} from "chart.js"; } 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 // Register ChartJS components
ChartJS.register( ChartJS.register(
@ -22,12 +27,28 @@ ChartJS.register(
Legend Legend
); );
interface ProductionCapacityProps { interface ProductionCapacityProps {
id: string;
type: string;
position: [number, number, number]; 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 // Chart data for a week
const chartData = { const defaultChartData = {
labels: ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"], // Days of the week labels: ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"], // Days of the week
datasets: [ datasets: [
{ {
@ -78,12 +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 ( return (
<Html position={[position[0], position[1], position[2]]} <Html position={[position[0], position[1], position[2]]}
scale={[0.5, 0.5, 0.5]} scale={[0.5, 0.5, 0.5]}
transform transform
sprite> sprite>
<div className="productionCapacity-wrapper card"> <div className="productionCapacity-wrapper card"
onClick={
() => setSelectedChartId({
id: id,
type: type
})
}>
<div className="headeproductionCapacityr-wrapper"> <div className="headeproductionCapacityr-wrapper">
<div className="header">Production Capacity</div> <div className="header">Production Capacity</div>
<div className="production-capacity"> <div className="production-capacity">
@ -104,7 +207,7 @@ const ProductionCapacity : React.FC<ProductionCapacityProps> = ({ position }) =>
</div>{" "} </div>{" "}
<div className="bar-chart charts"> <div className="bar-chart charts">
{/* Bar Chart */} {/* Bar Chart */}
<Bar data={chartData} options={chartOptions} /> <Bar data={Object.keys(measurements).length > 0 ? chartData : defaultChartData } options={chartOptions} />
</div> </div>
</div> </div>
</Html> </Html>

View File

@ -1,5 +1,5 @@
import { Html } from "@react-three/drei"; import { Html } from "@react-three/drei";
import React from "react"; import React, { useEffect, useMemo, useState } from "react";
import { Line } from "react-chartjs-2"; import { Line } from "react-chartjs-2";
import { import {
Chart as ChartJS, Chart as ChartJS,
@ -12,6 +12,10 @@ import {
ChartOptions, ChartOptions,
} from "chart.js"; } from "chart.js";
import { WavyIcon } from "../../../icons/3dChartIcons"; 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 // Register Chart.js components
ChartJS.register( ChartJS.register(
@ -36,9 +40,24 @@ const SmoothLineGraphComponent: React.FC<SmoothLineGraphProps> = ({
return <Line data={data} options={options} />; return <Line data={data} options={options} />;
}; };
interface ReturnOfInvestmentProps { interface ReturnOfInvestmentProps {
id: string;
type: string;
position: [number, number, number]; 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) // Improved sample data for the smooth curve graph (single day)
const graphData: ChartData<"line"> = { const graphData: ChartData<"line"> = {
labels: [ labels: [
@ -107,16 +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 ( return (
<Html position={[position[0], position[1], position[2]]} <Html position={[position[0], position[1], position[2]]}
scale={[0.5, 0.5, 0.5]} scale={[0.5, 0.5, 0.5]}
transform transform
zIndexRange={[1,0]}
sprite> sprite>
<div className="returnOfInvestment card"> <div className="returnOfInvestment card"
onClick={
() => setSelectedChartId({
id: id,
type: type
})
}>
<div className="header">Return of Investment</div> <div className="header">Return of Investment</div>
<div className="lineGraph charts"> <div className="lineGraph charts">
{/* Smooth curve graph with two datasets */} {/* Smooth curve graph with two datasets */}
<SmoothLineGraphComponent data={graphData} options={graphOptions} /> <SmoothLineGraphComponent data={Object.keys(measurements).length > 0 ? chartData : graphData} options={graphOptions} />
</div> </div>
<div className="returns-wrapper"> <div className="returns-wrapper">
<div className="icon"> <div className="icon">

View File

@ -1,28 +1,112 @@
import { Html } from "@react-three/drei"; 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"; // import image from "../../../../assets/image/temp/image.png";
interface StateWorkingProps { interface StateWorkingProps {
id:string;
type: string;
position: [number, number, number]; position: [number, number, number];
} }
const StateWorking: React.FC<StateWorkingProps> = ({ position }) => { const StateWorking: React.FC<StateWorkingProps> = ({ id, type, position }) => {
const datas = [ const { selectedChartId, setSelectedChartId } = useWidgetStore();
{ key: "Oil Tank:", value: "24/341" }, const { measurements: chartMeasurements, duration: chartDuration, name: widgetName } = useChartStore();
{ key: "Oil Refin:", value: 36.023 }, const [measurements, setmeasurements] = useState<any>({});
{ key: "Transmission:", value: 36.023 }, const [duration, setDuration] = useState("1h")
{ key: "Fuel:", value: 36732 }, const [name, setName] = useState("Widget")
{ key: "Power:", value: 1300 }, const [datas, setDatas] = useState<any>({});
{ key: "Time:", value: 13 - 9 - 2023 }, 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 ( return (
<Html position={[position[0], position[1], position[2]]} <Html position={[position[0], position[1], position[2]]}
scale={[0.5, 0.5, 0.5]} scale={[0.5, 0.5, 0.5]}
transform transform
zIndexRange={[1,0]}
sprite> sprite>
<div className="stateWorking-wrapper card"> <div className="stateWorking-wrapper card"
onClick={
() => setSelectedChartId({
id: id,
type: type
})
}>
<div className="header-wrapper"> <div className="header-wrapper">
<div className="header"> <div className="header">
<span>State</span> <span>State</span>
<span> <span>
Working <span>.</span> {datas?.input1 ? datas.input1 : 'input1'} <span>.</span>
</span> </span>
</div> </div>
<div className="img"> <div className="img">
@ -31,12 +115,36 @@ const StateWorking: React.FC<StateWorkingProps> = ({ position }) => {
</div> </div>
{/* Data */} {/* Data */}
<div className="data-wrapper"> <div className="data-wrapper">
{datas.map((data, index) => ( {/* {datas.map((data, index) => (
<div className="data-table" key={index}> <div className="data-table" key={index}>
<div className="data">{data.key}</div> <div className="data">{data.key}</div>
<div className="key">{data.value}</div> <div className="key">{data.value}</div>
</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>
</div> </div>
</Html> </Html>

View File

@ -1,5 +1,5 @@
import { Html } from "@react-three/drei"; import { Html } from "@react-three/drei";
import React from "react"; import React, { useEffect, useMemo, useState } from "react";
import { Line } from "react-chartjs-2"; import { Line } from "react-chartjs-2";
import { import {
Chart as ChartJS, Chart as ChartJS,
@ -14,6 +14,10 @@ import {
ChartOptions, ChartOptions,
} from "chart.js"; } from "chart.js";
import { ThroughputIcon } from "../../../icons/3dChartIcons"; 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 // Register Chart.js components
ChartJS.register( ChartJS.register(
@ -38,10 +42,25 @@ const LineGraphComponent: React.FC<LineGraphProps> = ({ data, options }) => {
}; };
interface ThroughputProps { interface ThroughputProps {
id: string;
type: string;
position: [number, number, number]; 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 // Sample data for the line graph
const graphData: ChartData<"line"> = { const graphData: ChartData<"line"> = {
@ -90,13 +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 ( return (
<Html position={[position[0], position[1], position[2]]} <Html position={[position[0], position[1], position[2]]}
scale={[0.5, 0.5, 0.5]} scale={[0.5, 0.5, 0.5]}
transform transform
zIndexRange={[1, 0]}
sprite> sprite>
<div className="throughput-wrapper"> <div className="throughput-wrapper"
<div className="header">Throughput</div> onClick={
() => setSelectedChartId({
id: id,
type: type
})
}>
<div className="header">{name}</div>
<div className="display-value"> <div className="display-value">
<div className="left"> <div className="left">
<div className="icon"> <div className="icon">
@ -119,7 +219,7 @@ const Throughput: React.FC<ThroughputProps> = ({ position }) => {
</div> </div>
<div className="line-graph"> <div className="line-graph">
{/* Line graph using react-chartjs-2 */} {/* Line graph using react-chartjs-2 */}
<LineGraphComponent data={graphData} options={graphOptions} /> <LineGraphComponent data={Object.keys(measurements).length > 0 ? chartData : graphData} options={graphOptions} />
</div> </div>
<div className="footer"> <div className="footer">
You made an extra <span className="value">$1256.13</span> this month 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 arch from "../../../assets/gltf-glb/arch.glb";
import door from "../../../assets/gltf-glb/door.glb"; import door from "../../../assets/gltf-glb/door.glb";
import window from "../../../assets/gltf-glb/window.glb"; import window from "../../../assets/gltf-glb/window.glb";
import { fetchAssets } from "../../../services/marketplace/fetchAssets";
import { useSelectedItem } from "../../../store/store"; import { useSelectedItem } from "../../../store/store";
interface AssetProp { interface AssetProp {
filename: string; filename: string;
thumbnail?: string; thumbnail?: string;
category: string; category: string;
description?: string; description?: string;
tags?: string; tags: string;
url?: String; url?: String;
uploadDate?: number; uploadDate?: number;
isArchieve?: boolean; isArchieve?: boolean;
@ -23,19 +24,58 @@ interface AssetProp {
price?: number; price?: number;
CreatedBy?: String; CreatedBy?: String;
} }
interface CategoryListProp {
assetImage?: string;
assetName?: string;
categoryImage: string;
category: string;
}
const Assets: React.FC = () => { const Assets: React.FC = () => {
const { setSelectedItem } = useSelectedItem(); const { setSelectedItem } = useSelectedItem();
const [searchValue, setSearchValue] = useState<string>(""); const [searchValue, setSearchValue] = useState<string>("");
const [selectedCategory, setSelectedCategory] = useState<string | null>(null); 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 handleSearchChange = (value: string) => {
const searchTerm = value.toLowerCase();
setSearchValue(value); 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", assetName: "Doors",
assetImage: "", assetImage: "",
@ -58,10 +98,8 @@ const Assets: React.FC = () => {
{ category: "Workstation", categoryImage: workStation }, { category: "Workstation", categoryImage: workStation },
{ category: "Machines", categoryImage: machines }, { category: "Machines", categoryImage: machines },
{ category: "Workers", categoryImage: worker }, { category: "Workers", categoryImage: worker },
], ]);
[] }, []);
);
const fetchCategoryAssets = async (asset: any) => { const fetchCategoryAssets = async (asset: any) => {
setSelectedCategory(asset); setSelectedCategory(asset);
if (asset === "Feneration") { if (asset === "Feneration") {
@ -70,60 +108,93 @@ const Assets: React.FC = () => {
filename: "arch", filename: "arch",
category: "Feneration", category: "Feneration",
url: arch, url: arch,
tags: "arch",
}, },
{ {
filename: "door", filename: "door",
category: "Feneration", category: "Feneration",
url: door, url: door,
thumbnail: feneration,
tags: "door",
}, },
{ {
filename: "window", filename: "window",
category: "Feneration", category: "Feneration",
url: window, url: window,
tags: "window",
}, },
]; ];
setFilteredAsset(localAssets); setCategoryAssets(localAssets);
setFiltereredAssets(localAssets);
} else { } else {
try { try {
const res = await getCategoryAsset(asset); const res = await getCategoryAsset(asset);
setFilteredAsset(res || []); // Ensure it's always an array setCategoryAssets(res);
} catch (error) { } setFiltereredAssets(res);
} catch (error) {}
} }
}; };
useEffect(() => { }, [filteredAsset]);
return ( return (
<div className="assets-container"> <div className="assets-container">
<Search onChange={handleSearchChange} /> <Search onChange={handleSearchChange} />
{searchValue ? ( {searchValue ? (
<div className="searched-content"> <div className="assets-result">
<p>Results for "{searchValue}"</p> <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> </div>
) : selectedCategory ? ( ) : selectedCategory ? (
<div className="assets-wrapper"> <div className="assets-wrapper">
{/* Back Button */}
<div <div
className="back-button" className="back-button"
onClick={() => { onClick={() => {
setSelectedCategory(null); setSelectedCategory(null);
setFilteredAsset([]); setCategoryAssets([]);
}} }}
> >
Back Back
</div> </div>
<h2>{selectedCategory}</h2> <h2>{selectedCategory}</h2>
<div className="assets-container"> <div className="assets-container">
{filteredAsset && {categoryAssets &&
filteredAsset?.map((asset: any, index: number) => ( categoryAssets?.map((asset: any, index: number) => (
<div key={index} className="assets"> <div key={index} className="assets">
{asset?.thumbnail && ( <img
<img src={asset?.thumbnail}
src={asset?.thumbnail} alt={asset.filename}
alt={asset.filename} className="asset-image"
className="asset-image" onPointerDown={() =>
onPointerDown={() => setSelectedItem({ name: asset.filename, id: asset.modelfileID })} setSelectedItem({
/> name: asset.filename,
)} id: asset.modelfileID,
})
}
/>
<div className="asset-name"> <div className="asset-name">
{asset.filename {asset.filename
.split("_") .split("_")

View File

@ -5,11 +5,13 @@ import { useSelectedZoneStore } from "../../../../store/useZoneStore";
import { getTemplateData } from "../../../../services/realTimeVisulization/zoneData/getTemplate"; import { getTemplateData } from "../../../../services/realTimeVisulization/zoneData/getTemplate";
import { deleteTemplateApi } from "../../../../services/realTimeVisulization/zoneData/deleteTemplate"; import { deleteTemplateApi } from "../../../../services/realTimeVisulization/zoneData/deleteTemplate";
import { loadTempleteApi } from "../../../../services/realTimeVisulization/zoneData/loadTemplate"; import { loadTempleteApi } from "../../../../services/realTimeVisulization/zoneData/loadTemplate";
import { useSocketStore } from "../../../../store/store";
const Templates = () => { const Templates = () => {
const { templates, removeTemplate } = useTemplateStore(); const { templates, removeTemplate } = useTemplateStore();
const { setTemplates } = useTemplateStore(); const { setTemplates } = useTemplateStore();
const { setSelectedZone, selectedZone } = useSelectedZoneStore(); const { setSelectedZone, selectedZone } = useSelectedZoneStore();
const { visualizationSocket } = useSocketStore();
useEffect(() => { useEffect(() => {
async function templateData() { async function templateData() {
@ -30,12 +32,18 @@ const Templates = () => {
try { try {
const email = localStorage.getItem("email") || ""; const email = localStorage.getItem("email") || "";
const organization = email?.split("@")[1]?.split(".")[0]; const organization = email?.split("@")[1]?.split(".")[0];
let deleteTemplate = {
let response = await deleteTemplateApi(id, organization); organization: organization,
templateID: id,
if (response.message === "Template deleted successfully") {
removeTemplate(id);
} }
if (visualizationSocket) {
visualizationSocket.emit("v2:viz-template:deleteTemplate", deleteTemplate)
}
removeTemplate(id);
// let response = await deleteTemplateApi(id, organization);
// if (response.message === "Template deleted successfully") {
// }
} catch (error) { } catch (error) {
console.error("Error deleting template:", error); console.error("Error deleting template:", error);
} }
@ -48,9 +56,20 @@ const Templates = () => {
const email = localStorage.getItem("email") || ""; const email = localStorage.getItem("email") || "";
const organization = email?.split("@")[1]?.split(".")[0]; const organization = email?.split("@")[1]?.split(".")[0];
let response = await loadTempleteApi(template.id, selectedZone.zoneId, organization); let loadingTemplate = {
organization: organization,
zoneId: selectedZone.zoneId,
templateID: template.id,
}
console.log('template: ', template);
console.log('loadingTemplate: ', loadingTemplate);
if (response.message === "Template placed in Zone") { if (visualizationSocket) {
visualizationSocket.emit("v2:viz-template:addToZone", loadingTemplate)
}
// let response = await loadTempleteApi(template.id, selectedZone.zoneId, organization);
// if (response.message === "Template placed in Zone") {
setSelectedZone({ setSelectedZone({
panelOrder: template.panelOrder, panelOrder: template.panelOrder,
activeSides: Array.from(new Set(template.panelOrder)), // No merging with previous `activeSides` activeSides: Array.from(new Set(template.panelOrder)), // No merging with previous `activeSides`
@ -64,7 +83,7 @@ const Templates = () => {
useDroppedObjectsStore.getState().addObject(selectedZone.zoneName, val); useDroppedObjectsStore.getState().addObject(selectedZone.zoneName, val);
}); });
} }
} // }
} catch (error) { } catch (error) {
console.error("Error loading template:", error); console.error("Error loading template:", error);
} }

View File

@ -59,9 +59,11 @@ const ProgressBarWidget = ({
id, id,
title, title,
data, data,
type
}: { }: {
id: string; id: string;
title: string; title: string;
type: string;
data: any; data: any;
}) => { }) => {
const { setDraggedAsset } = useWidgetStore((state) => state); const { setDraggedAsset } = useWidgetStore((state) => state);
@ -72,7 +74,7 @@ const ProgressBarWidget = ({
draggable draggable
onDragStart={() => { onDragStart={() => {
setDraggedAsset({ setDraggedAsset({
type: "progress", type: type,
id, id,
title, title,
panel: "top", panel: "top",
@ -99,6 +101,7 @@ const ProgressBarWidget = ({
</div> </div>
); );
}; };
console.log(chartTypes, "chartTypes");
const Widgets2D = () => { const Widgets2D = () => {
return ( return (
@ -123,6 +126,7 @@ const Widgets2D = () => {
{ key: "units", value: 1000, description: "Initial stock" }, { key: "units", value: 1000, description: "Initial stock" },
], ],
}} }}
type={"progress 1"}
/> />
<ProgressBarWidget <ProgressBarWidget
id="widget-8" id="widget-8"
@ -133,6 +137,7 @@ const Widgets2D = () => {
{ key: "units", value: 500, description: "Additional stock" }, { key: "units", value: 500, description: "Additional stock" },
], ],
}} }}
type={"progress 2"}
/> />
</div> </div>
</div> </div>

View File

@ -33,6 +33,7 @@ const RenderAnalysisInputs: React.FC<InputRendererProps> = ({
label={preset.inputs.label} label={preset.inputs.label}
min={0} min={0}
max={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 InputRange from "../../../ui/inputs/InputRange";
import InputToggle from "../../../ui/inputs/InputToggle"; import InputToggle from "../../../ui/inputs/InputToggle";
import { AI_Icon } from "../../../icons/ExportCommonIcons"; import { AI_Icon } from "../../../icons/ExportCommonIcons";
@ -6,17 +6,20 @@ import LabeledButton from "../../../ui/inputs/LabledButton";
import { import {
useAzimuth, useAzimuth,
useElevation, useElevation,
useLimitDistance,
useRenderDistance, useRenderDistance,
useResetCamera, useResetCamera,
useRoofVisibility, useRoofVisibility,
useSelectedWallItem, useSelectedWallItem,
useShadows, useShadows,
useSocketStore, useSocketStore,
useTileDistance,
useToggleView, useToggleView,
useWallVisibility, useWallVisibility,
} from "../../../../store/store"; } from "../../../../store/store";
import { setEnvironment } from "../../../../services/factoryBuilder/environment/setEnvironment"; import { setEnvironment } from "../../../../services/factoryBuilder/environment/setEnvironment";
import * as CONSTANTS from "../../../../types/world/worldConstants"; import * as CONSTANTS from "../../../../types/world/worldConstants";
import { validateBBox } from "@turf/helpers";
const GlobalProperties: React.FC = () => { const GlobalProperties: React.FC = () => {
const { toggleView, setToggleView } = useToggleView(); const { toggleView, setToggleView } = useToggleView();
const { selectedWallItem, setSelectedWallItem } = useSelectedWallItem(); const { selectedWallItem, setSelectedWallItem } = useSelectedWallItem();
@ -27,26 +30,95 @@ const GlobalProperties: React.FC = () => {
const { elevation, setElevation } = useElevation(); const { elevation, setElevation } = useElevation();
const { azimuth, setAzimuth } = useAzimuth(); const { azimuth, setAzimuth } = useAzimuth();
const { renderDistance, setRenderDistance } = useRenderDistance(); const { renderDistance, setRenderDistance } = useRenderDistance();
const { setPlaneValue, setGridValue, planeValue, gridValue } =
useTileDistance();
useEffect(() => {
console.log(gridValue, planeValue, "values");
}, [gridValue, planeValue]);
const { socket } = useSocketStore(); const { socket } = useSocketStore();
const [limitDistance, setLimitDistance] = useState(false); const { limitDistance, setLimitDistance } = useLimitDistance();
const [distance, setDistance] = useState<number>(30); const [distance, setDistance] = useState<number>(40);
useEffect(() => {}, [limitDistance]);
const [limitGridDistance, setLimitGridDistance] = useState(false); 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); 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) { function updateDistance(value: number) {
setDistance(value); setDistance(value);
setRenderDistance(value); setRenderDistance(value);
} }
function updateGridDistance(value: number) { function updateGridDistance(value: number) {
setGridDistance(value); 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 // Function to toggle roof visibility
const changeRoofVisibility = async () => { const changeRoofVisibility = async () => {
const email = localStorage.getItem("email"); const email = localStorage.getItem("email");
@ -58,7 +130,9 @@ const GlobalProperties: React.FC = () => {
localStorage.getItem("userId")!, localStorage.getItem("userId")!,
wallVisibility, wallVisibility,
!roofVisibility, !roofVisibility,
shadows shadows,
renderDistance,
limitDistance
); );
// //
@ -85,7 +159,9 @@ const GlobalProperties: React.FC = () => {
localStorage.getItem("userId")!, localStorage.getItem("userId")!,
!wallVisibility, !wallVisibility,
roofVisibility, roofVisibility,
shadows shadows,
renderDistance,
limitDistance
); );
// //
@ -112,7 +188,9 @@ const GlobalProperties: React.FC = () => {
localStorage.getItem("userId")!, localStorage.getItem("userId")!,
wallVisibility, wallVisibility,
roofVisibility, roofVisibility,
!shadows !shadows,
renderDistance,
limitDistance
); );
// //
@ -184,18 +262,24 @@ const GlobalProperties: React.FC = () => {
inputKey="4" inputKey="4"
label="Limit Render Distance" label="Limit Render Distance"
value={limitDistance} value={limitDistance}
onClick={() => { // onClick={() => {
setLimitDistance(!limitDistance); // setLimitDistance(!limitDistance);
// // setDistance(75);
// // setRenderDistance(75);
// }}
onClick={async () => {
await limitRenderDistance(); // Call the function here
}} }}
/> />
<InputRange <InputRange
label="Distance" label="Distance"
disabled={!limitDistance} disabled={!limitDistance}
value={distance} value={renderDistance}
key={"5"}
min={CONSTANTS.distanceConfig.minDistance} min={CONSTANTS.distanceConfig.minDistance}
max={CONSTANTS.distanceConfig.maxDistance} max={CONSTANTS.distanceConfig.maxDistance}
onChange={(value: number) => updateDistance(value)} onChange={(value: number) => updateDistance(value)}
onPointerUp={updatedDist}
key={"6"}
/> />
<div className="split"></div> <div className="split"></div>
@ -213,7 +297,10 @@ const GlobalProperties: React.FC = () => {
disabled={!limitGridDistance} disabled={!limitGridDistance}
value={gridDistance} value={gridDistance}
key={"7"} key={"7"}
min={1}
max={5}
onChange={(value: number) => updateGridDistance(value)} onChange={(value: number) => updateGridDistance(value)}
onPointerUp={updatedGrid}
/> />
</div> </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 FlotingWidgetInput = (props: Props) => {
const [widgetName, setWidgetName] = useState('Widget'); const [widgetName, setWidgetName] = useState('Widget');
const { setMeasurements, updateDuration, updateName } = useChartStore(); const { setFlotingMeasurements, updateFlotingDuration, updateHeader } = useChartStore();
const [duration, setDuration] = useState('1h') const [duration, setDuration] = useState('1h')
const [dropDowndata, setDropDownData] = useState({}); const [dropDowndata, setDropDownData] = useState({});
const [selections, setSelections] = useState<Record<string, { name: string; fields: string }>>({}); const [selections, setSelections] = useState<Record<string, { name: string; fields: string }>>({});
@ -43,11 +43,13 @@ const FlotingWidgetInput = (props: Props) => {
const fetchSavedInputes = async () => { const fetchSavedInputes = async () => {
if (selectedChartId.id !== "") { if (selectedChartId.id !== "") {
try { 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) { if (response.status === 200) {
console.log(response.data);
setSelections(response.data.Data.measurements) setSelections(response.data.Data.measurements)
setDuration(response.data.Data.duration) setDuration(response.data.Data.duration)
setWidgetName(response.data.widgetName) setWidgetName(response.data.header)
} else { } else {
console.log("Unexpected response:", response); console.log("Unexpected response:", response);
} }
@ -63,9 +65,9 @@ const FlotingWidgetInput = (props: Props) => {
// Sync Zustand state when component mounts // Sync Zustand state when component mounts
useEffect(() => { useEffect(() => {
setMeasurements(selections); setFlotingMeasurements(selections);
updateDuration(duration); updateFlotingDuration(duration);
updateName(widgetName); updateHeader(widgetName);
}, [selections, duration, widgetName]); }, [selections, duration, widgetName]);
@ -76,8 +78,7 @@ const FlotingWidgetInput = (props: Props) => {
zoneId: selectedZone.zoneId, zoneId: selectedZone.zoneId,
widget: { widget: {
id: selectedChartId.id, id: selectedChartId.id,
panel: selectedChartId.panel, header: inputName,
widgetName: inputName,
Data: { Data: {
measurements: inputMeasurement, measurements: inputMeasurement,
duration: inputDuration duration: inputDuration
@ -135,7 +136,7 @@ const FlotingWidgetInput = (props: Props) => {
<div className="inputs-wrapper"> <div className="inputs-wrapper">
<div className="datas"> <div className="datas">
<div className="datas__label">Title</div> <div className="datas__label">Title</div>
<RenameInput value={selectedChartId?.title || "untited"} onRename={handleNameChange}/> <RenameInput value={selectedChartId?.header || "untited"} onRename={handleNameChange}/>
</div> </div>
{[...Array(6)].map((_, index) => { {[...Array(6)].map((_, index) => {
const inputKey = `input${index + 1}`; 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> <div className="datas__label">Title</div>
<RenameInput value={selectedChartId?.title || "untited"} onRename={handleNameChange}/> <RenameInput value={selectedChartId?.title || "untited"} onRename={handleNameChange}/>
</div> </div>
{[...Array(6)].map((_, index) => { {[...Array(4)].map((_, index) => {
const inputKey = `input${index + 1}`; const inputKey = `input${index + 1}`;
return ( return (
<div key={index} className="datas"> <div key={index} className="datas">

View File

@ -1,78 +1,177 @@
import React, { useEffect, useState } from 'react' import React, { useEffect, useState } from "react";
import MultiLevelDropdown from '../../../../ui/inputs/MultiLevelDropDown' import MultiLevelDropdown from "../../../../ui/inputs/MultiLevelDropDown";
import { AddIcon } from '../../../../icons/ExportCommonIcons' import { AddIcon } from "../../../../icons/ExportCommonIcons";
import axios from 'axios' 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 PieChartInput = (props: Props) => {
const [dropDowndata, setDropDownData] = useState({}) const [widgetName, setWidgetName] = useState('Widget');
const [selections, setSelections] = useState<Record<string, {name: string, fields: string}>>({}) const { setMeasurements, updateDuration, updateName } = useChartStore();
const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL; 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(() => { useEffect(() => {
const fetchZoneData = async () => { const fetchZoneData = async () => {
try { try {
const response = await axios.get(`http://${iotApiUrl}/getinput`); const response = await axios.get(`http://${iotApiUrl}/getinput`);
if (response.status === 200) { if (response.status === 200) {
console.log('dropdown data:', response.data); // console.log("dropdown data:", response.data);
setDropDownData(response.data) setDropDownData(response.data);
} else { } else {
console.log('Unexpected response:', response); console.log("Unexpected response:", response);
} }
} catch (error) { } catch (error) {
console.error('There was an error!', 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
};
}
});
}; };
fetchZoneData();
}, []);
return ( useEffect(() => {
<> const fetchSavedInputes = async () => {
<div className="inputs-wrapper"> if (selectedChartId.id !== "") {
{[...Array(3)].map((_, index) => { try {
const inputKey = `input${index+1}`; const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/WidgetData/${selectedChartId.id}/${organization}`);
return ( if (response.status === 200) {
<div key={index} className="datas"> setSelections(response.data.Data.measurements)
<div className="datas__label">Input {index+1}</div> setDuration(response.data.Data.duration)
<div className="datas__class"> setWidgetName(response.data.widgetName)
<MultiLevelDropdown } else {
data={dropDowndata} console.log("Unexpected response:", response);
onSelect={(selectedData) => handleSelect(inputKey, selectedData)} }
onUnselect={() => handleSelect(inputKey, null)} } catch (error) {
selectedValue={selections[inputKey]} console.error("There was an error!", error);
/> }
<div className="icon"> }
<AddIcon /> }
</div>
</div>
</div>
);
})}
</div>
<div>
</div>
</>
)
}
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 MultiLevelDropDown from "../../../../ui/inputs/MultiLevelDropDown";
import LineGrapInput from "../IotInputCards/LineGrapInput"; import LineGrapInput from "../IotInputCards/LineGrapInput";
import RenameInput from "../../../../ui/inputs/RenameInput"; import RenameInput from "../../../../ui/inputs/RenameInput";
import InputSelecterComponent from "../IotInputCards/InputSelecterComponent";
// Define the data structure for demonstration purposes // Define the data structure for demonstration purposes
const DATA_STRUCTURE = { const DATA_STRUCTURE = {
@ -118,7 +119,7 @@ const Data = () => {
}; };
}); });
}; };
// console.log("selectedChartId", selectedChartId);
return ( return (
<div className="dataSideBar"> <div className="dataSideBar">
@ -133,8 +134,7 @@ const Data = () => {
{ {
chartDataGroups[selectedChartId?.id] && chartDataGroups[selectedChartId?.id] &&
<> <>
<div className="sideBarHeader">2D Widget Input</div> <InputSelecterComponent />
<LineGrapInput />
</> </>
} }

View File

@ -1,10 +1,11 @@
import React, { useState } from "react"; import React, { useEffect, useState } from "react";
import RenderOverlay from "./Overlay"; import RenderOverlay from "./Overlay";
import { ArrowIcon, CloseIcon } from "../icons/ExportCommonIcons"; import { ArrowIcon, CloseIcon } from "../icons/ExportCommonIcons";
import { AccessOption, User } from "../../types/users"; import { AccessOption, User } from "../../types/users";
import RegularDropDown from "../ui/inputs/RegularDropDown"; import RegularDropDown from "../ui/inputs/RegularDropDown";
import { access } from "fs"; import { access } from "fs";
import MultiEmailInvite from "../ui/inputs/MultiEmailInvite"; import MultiEmailInvite from "../ui/inputs/MultiEmailInvite";
import { useActiveUsers } from "../../store/store";
interface UserListTemplateProps { interface UserListTemplateProps {
user: User; user: User;
@ -57,6 +58,10 @@ interface CollaborateProps {
const CollaborationPopup: React.FC<CollaborateProps> = ({ const CollaborationPopup: React.FC<CollaborateProps> = ({
setUserManagement, setUserManagement,
}) => { }) => {
const { activeUsers } = useActiveUsers();
useEffect(() => {
console.log("activeUsers: ", activeUsers);
}, [activeUsers]);
const userName = localStorage.getItem("userName") || "Anonymous"; const userName = localStorage.getItem("userName") || "Anonymous";
const users = [ const users = [
{ {

View File

@ -27,12 +27,16 @@ import {
useMovePoint, useMovePoint,
useRefTextUpdate, useRefTextUpdate,
useSelectedWallItem, useSelectedWallItem,
useSocketStore,
useToggleView, useToggleView,
useToolMode, useToolMode,
useTransformMode, useTransformMode,
} from "../../store/store"; } from "../../store/store";
import useToggleStore from "../../store/useUIToggleStore"; import useToggleStore from "../../store/useUIToggleStore";
import { use3DWidget, useFloatingWidget } from "../../store/useDroppedObjectsStore"; import {
use3DWidget,
useFloatingWidget,
} from "../../store/useDroppedObjectsStore";
const Tools: React.FC = () => { const Tools: React.FC = () => {
const { templates } = useTemplateStore(); const { templates } = useTemplateStore();
@ -42,13 +46,13 @@ const Tools: React.FC = () => {
const dropdownRef = useRef<HTMLDivElement>(null); const dropdownRef = useRef<HTMLDivElement>(null);
const [openDrop, setOpenDrop] = useState(false); const [openDrop, setOpenDrop] = useState(false);
const { visualizationSocket } = useSocketStore();
const { activeModule } = useModuleStore(); const { activeModule } = useModuleStore();
const { isPlaying, setIsPlaying } = usePlayButtonStore(); const { isPlaying, setIsPlaying } = usePlayButtonStore();
const { addTemplate } = useTemplateStore(); const { addTemplate } = useTemplateStore();
const { selectedZone } = useSelectedZoneStore(); const { selectedZone } = useSelectedZoneStore();
const { floatingWidget } = useFloatingWidget() const { floatingWidget } = useFloatingWidget();
const { widgets3D } = use3DWidget() const { widgets3D } = use3DWidget();
// wall options // wall options
const { toggleView, setToggleView } = useToggleView(); const { toggleView, setToggleView } = useToggleView();
@ -71,7 +75,7 @@ const Tools: React.FC = () => {
: true : true
); );
}, []); }, []);
useEffect(() => { }, [activeModule]); useEffect(() => {}, [activeModule]);
useEffect(() => { useEffect(() => {
setActiveTool(activeSubTool); setActiveTool(activeSubTool);
setActiveSubTool(activeSubTool); setActiveSubTool(activeSubTool);
@ -213,8 +217,9 @@ const Tools: React.FC = () => {
<div className="activeDropicon"> <div className="activeDropicon">
{activeSubTool == "cursor" && ( {activeSubTool == "cursor" && (
<div <div
className={`tool-button ${activeTool === "cursor" ? "active" : "" className={`tool-button ${
}`} activeTool === "cursor" ? "active" : ""
}`}
onClick={() => { onClick={() => {
setActiveTool("cursor"); setActiveTool("cursor");
}} }}
@ -224,8 +229,9 @@ const Tools: React.FC = () => {
)} )}
{activeSubTool == "free-hand" && ( {activeSubTool == "free-hand" && (
<div <div
className={`tool-button ${activeTool === "free-hand" ? "active" : "" className={`tool-button ${
}`} activeTool === "free-hand" ? "active" : ""
}`}
onClick={() => { onClick={() => {
setActiveTool("free-hand"); setActiveTool("free-hand");
}} }}
@ -235,8 +241,9 @@ const Tools: React.FC = () => {
)} )}
{activeSubTool == "delete" && ( {activeSubTool == "delete" && (
<div <div
className={`tool-button ${activeTool === "delete" ? "active" : "" className={`tool-button ${
}`} activeTool === "delete" ? "active" : ""
}`}
onClick={() => { onClick={() => {
setActiveTool("delete"); setActiveTool("delete");
}} }}
@ -308,8 +315,9 @@ const Tools: React.FC = () => {
<div className="split"></div> <div className="split"></div>
<div className="draw-tools"> <div className="draw-tools">
<div <div
className={`tool-button ${activeTool === "draw-wall" ? "active" : "" className={`tool-button ${
}`} activeTool === "draw-wall" ? "active" : ""
}`}
onClick={() => { onClick={() => {
setActiveTool("draw-wall"); setActiveTool("draw-wall");
}} }}
@ -318,8 +326,9 @@ const Tools: React.FC = () => {
<WallIcon isActive={activeTool === "draw-wall"} /> <WallIcon isActive={activeTool === "draw-wall"} />
</div> </div>
<div <div
className={`tool-button ${activeTool === "draw-zone" ? "active" : "" className={`tool-button ${
}`} activeTool === "draw-zone" ? "active" : ""
}`}
onClick={() => { onClick={() => {
setActiveTool("draw-zone"); setActiveTool("draw-zone");
}} }}
@ -328,8 +337,9 @@ const Tools: React.FC = () => {
<ZoneIcon isActive={activeTool === "draw-zone"} /> <ZoneIcon isActive={activeTool === "draw-zone"} />
</div> </div>
<div <div
className={`tool-button ${activeTool === "draw-aisle" ? "active" : "" className={`tool-button ${
}`} activeTool === "draw-aisle" ? "active" : ""
}`}
onClick={() => { onClick={() => {
setActiveTool("draw-aisle"); setActiveTool("draw-aisle");
}} }}
@ -338,8 +348,9 @@ const Tools: React.FC = () => {
<AsileIcon isActive={activeTool === "draw-aisle"} /> <AsileIcon isActive={activeTool === "draw-aisle"} />
</div> </div>
<div <div
className={`tool-button ${activeTool === "draw-floor" ? "active" : "" className={`tool-button ${
}`} activeTool === "draw-floor" ? "active" : ""
}`}
onClick={() => { onClick={() => {
setActiveTool("draw-floor"); setActiveTool("draw-floor");
}} }}
@ -355,8 +366,9 @@ const Tools: React.FC = () => {
<div className="split"></div> <div className="split"></div>
<div className="draw-tools"> <div className="draw-tools">
<div <div
className={`tool-button ${activeTool === "measure" ? "active" : "" className={`tool-button ${
}`} activeTool === "measure" ? "active" : ""
}`}
onClick={() => { onClick={() => {
setActiveTool("measure"); setActiveTool("measure");
}} }}
@ -372,8 +384,9 @@ const Tools: React.FC = () => {
<div className="split"></div> <div className="split"></div>
<div className="draw-tools"> <div className="draw-tools">
<div <div
className={`tool-button ${activeTool === "pen" ? "active" : "" className={`tool-button ${
}`} activeTool === "pen" ? "active" : ""
}`}
onClick={() => { onClick={() => {
setActiveTool("pen"); setActiveTool("pen");
}} }}
@ -390,13 +403,13 @@ const Tools: React.FC = () => {
<div <div
className={`tool-button`} className={`tool-button`}
onClick={() => { onClick={() => {
handleSaveTemplate({ handleSaveTemplate({
addTemplate, addTemplate,
floatingWidget, floatingWidget,
widgets3D, widgets3D,
selectedZone, selectedZone,
templates, templates,
visualizationSocket
}) })
} }
} }
@ -409,8 +422,9 @@ const Tools: React.FC = () => {
<div className="split"></div> <div className="split"></div>
<div className="general-options"> <div className="general-options">
<div <div
className={`tool-button ${activeTool === "comment" ? "active" : "" className={`tool-button ${
}`} activeTool === "comment" ? "active" : ""
}`}
onClick={() => { onClick={() => {
setActiveTool("comment"); setActiveTool("comment");
}} }}
@ -419,10 +433,12 @@ const Tools: React.FC = () => {
</div> </div>
{toggleThreeD && ( {toggleThreeD && (
<div <div
className={`tool-button ${activeTool === "play" ? "active" : "" className={`tool-button ${
}`} activeTool === "play" ? "active" : ""
}`}
onClick={() => { onClick={() => {
setIsPlaying(!isPlaying); setIsPlaying(!isPlaying);
setActiveTool("play");
}} }}
> >
<PlayIcon isActive={activeTool === "play"} /> <PlayIcon isActive={activeTool === "play"} />
@ -433,14 +449,19 @@ const Tools: React.FC = () => {
<> <>
<div className="split"></div> <div className="split"></div>
<div <div
className={`toggle-threed-button${toggleThreeD ? " toggled" : "" className={`toggle-threed-button${
}`} toggleThreeD ? " toggled" : ""
}`}
onClick={toggleSwitch} onClick={toggleSwitch}
> >
<div className={`toggle-option${!toggleThreeD ? " active" : ""}`}> <div
className={`toggle-option${!toggleThreeD ? " active" : ""}`}
>
2d 2d
</div> </div>
<div className={`toggle-option${toggleThreeD ? " active" : ""}`}> <div
className={`toggle-option${toggleThreeD ? " active" : ""}`}
>
3d 3d
</div> </div>
</div> </div>

View File

@ -1,4 +1,4 @@
import React from "react"; import React, { useEffect } from "react";
import { import {
CleanPannel, CleanPannel,
EyeIcon, EyeIcon,
@ -6,6 +6,8 @@ import {
} from "../../icons/RealTimeVisulationIcons"; } from "../../icons/RealTimeVisulationIcons";
import { panelData } from "../../../services/realTimeVisulization/zoneData/panel"; import { panelData } from "../../../services/realTimeVisulization/zoneData/panel";
import { AddIcon } from "../../icons/ExportCommonIcons"; import { AddIcon } from "../../icons/ExportCommonIcons";
import { deletePanelApi } from "../../../services/realTimeVisulization/zoneData/deletePanel";
import { useSocketStore } from "../../../store/store";
// Define the type for `Side` // Define the type for `Side`
type Side = "top" | "bottom" | "left" | "right"; type Side = "top" | "bottom" | "left" | "right";
@ -16,7 +18,6 @@ interface ButtonsProps {
zoneName: string; zoneName: string;
activeSides: Side[]; activeSides: Side[];
panelOrder: Side[]; panelOrder: Side[];
lockedPanels: Side[]; lockedPanels: Side[];
zoneId: string; zoneId: string;
zoneViewPortTarget: number[]; zoneViewPortTarget: number[];
@ -34,7 +35,7 @@ interface ButtonsProps {
zoneName: string; zoneName: string;
activeSides: Side[]; activeSides: Side[];
panelOrder: Side[]; panelOrder: Side[];
lockedPanels: Side[]; lockedPanels: Side[];
zoneId: string; zoneId: string;
zoneViewPortTarget: number[]; zoneViewPortTarget: number[];
@ -58,6 +59,9 @@ const AddButtons: React.FC<ButtonsProps> = ({
setHiddenPanels, setHiddenPanels,
hiddenPanels, hiddenPanels,
}) => { }) => {
const { visualizationSocket } = useSocketStore();
// Local state to track hidden panels // Local state to track hidden panels
// Function to toggle lock/unlock a panel // Function to toggle lock/unlock a panel
@ -103,9 +107,13 @@ const AddButtons: React.FC<ButtonsProps> = ({
}; };
// Function to handle "+" button click // Function to handle "+" button click
const handlePlusButtonClick = (side: Side) => { const handlePlusButtonClick = async (side: Side) => {
if (selectedZone.activeSides.includes(side)) { if (selectedZone.activeSides.includes(side)) {
// If the panel is already active, remove all widgets and close the panel // Panel already exists: Remove widgets from that side and update activeSides
const email = localStorage.getItem("email") || "";
const organization = email?.split("@")[1]?.split(".")[0]; // Fallback value
// Remove all widgets associated with the side and update active sides
const cleanedWidgets = selectedZone.widgets.filter( const cleanedWidgets = selectedZone.widgets.filter(
(widget) => widget.panel !== side (widget) => widget.panel !== side
); );
@ -118,44 +126,68 @@ const AddButtons: React.FC<ButtonsProps> = ({
panelOrder: newActiveSides, panelOrder: newActiveSides,
}; };
// Delete the selectedZone state let deletePanel = {
organization: organization,
panelName: side,
zoneId: selectedZone.zoneId
}
if (visualizationSocket) {
visualizationSocket.emit("v2:viz-panel:delete", deletePanel)
}
setSelectedZone(updatedZone); setSelectedZone(updatedZone);
// API call to delete the panel
// try {
// const response = await deletePanelApi(selectedZone.zoneId, side, organization);
//
// if (response.message === "Panel deleted successfully") {
// } else {
//
// }
// } catch (error) {
//
// }
} else { } else {
const updatePanelData = async () => { // Panel does not exist: Create panel
try { try {
// Get email and organization safely // Get email and organization safely with a default fallback
const email = localStorage.getItem("email") || ""; const email = localStorage.getItem("email") || "";
const organization = email?.split("@")[1]?.split(".")[0] || "defaultOrg"; // Fallback value const organization = email?.split("@")[1]?.split(".")[0];
// Prevent duplicate side entries // Prevent duplicate side entries
const newActiveSides = selectedZone.activeSides.includes(side) const newActiveSides = selectedZone.activeSides.includes(side)
? [...selectedZone.activeSides] ? [...selectedZone.activeSides]
: [...selectedZone.activeSides, side]; : [...selectedZone.activeSides, side];
const updatedZone = { const updatedZone = {
...selectedZone, ...selectedZone,
activeSides: newActiveSides, activeSides: newActiveSides,
panelOrder: newActiveSides, panelOrder: newActiveSides,
}; };
let addPanel = {
// API call organization: organization,
const response = await panelData(organization, selectedZone.zoneId, newActiveSides); zoneId: selectedZone.zoneId,
panelOrder: newActiveSides
// Update state
setSelectedZone(updatedZone);
} catch (error) {
} }
}; if (visualizationSocket) {
visualizationSocket.emit("v2:viz-panel:add", addPanel)
}
setSelectedZone(updatedZone);
// API call to create panels
// const response = await panelData(organization, selectedZone.zoneId, newActiveSides);
//
updatePanelData(); // Call the async function // if (response.message === "Panels created successfully") {
// } else {
//
// }
} catch (error) {
}
} }
}; };
return ( return (
<> <>
<div> <div>

View File

@ -1,8 +1,11 @@
import React, { useEffect, useRef, useState, useCallback } from "react"; 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 { MoveArrowLeft, MoveArrowRight } from "../../icons/SimulationIcons";
import { InfoIcon } from "../../icons/ExportCommonIcons"; import { InfoIcon } from "../../icons/ExportCommonIcons";
import { useDroppedObjectsStore, useFloatingWidget } from "../../../store/useDroppedObjectsStore"; import {
useDroppedObjectsStore,
useFloatingWidget,
} from "../../../store/useDroppedObjectsStore";
import { getSelect2dZoneData } from "../../../services/realTimeVisulization/zoneData/getSelect2dZoneData"; import { getSelect2dZoneData } from "../../../services/realTimeVisulization/zoneData/getSelect2dZoneData";
import { getFloatingZoneData } from "../../../services/realTimeVisulization/zoneData/getFloatingData"; import { getFloatingZoneData } from "../../../services/realTimeVisulization/zoneData/getFloatingData";
import { get3dWidgetZoneData } from "../../../services/realTimeVisulization/zoneData/get3dWidgetData"; import { get3dWidgetZoneData } from "../../../services/realTimeVisulization/zoneData/get3dWidgetData";
@ -63,14 +66,15 @@ const DisplayZone: React.FC<DisplayZoneProps> = ({
selectedZone, selectedZone,
setSelectedZone, setSelectedZone,
}) => { }) => {
// Refs // Ref for the container element
const containerRef = useRef<HTMLDivElement | null>(null); const containerRef = useRef<HTMLDivElement | null>(null);
const scrollContainerRef = useRef<HTMLDivElement | null>(null); const scrollContainerRef = useRef<HTMLDivElement | null>(null);
// State to track overflow visibility // State to track overflow visibility
const [showLeftArrow, setShowLeftArrow] = useState(false); const [showLeftArrow, setShowLeftArrow] = useState(false);
const [showRightArrow, setShowRightArrow] = useState(false); const [showRightArrow, setShowRightArrow] = useState(false);
const { floatingWidget, setFloatingWidget } = useFloatingWidget(); const { floatingWidget, setFloatingWidget } = useFloatingWidget()
const{setSelectedChartId}=useWidgetStore()
// Function to calculate overflow state // Function to calculate overflow state
const updateOverflowState = useCallback(() => { const updateOverflowState = useCallback(() => {
@ -147,13 +151,15 @@ const DisplayZone: React.FC<DisplayZoneProps> = ({
if (selectedZone?.zoneId === zoneId) { if (selectedZone?.zoneId === zoneId) {
return; return;
} }
setSelectedChartId(null)
const email = localStorage.getItem("email") || ""; const email = localStorage.getItem("email") || "";
const organization = email?.split("@")[1]?.split(".")[0]; const organization = email?.split("@")[1]?.split(".")[0];
let response = await getSelect2dZoneData(zoneId, organization); let response = await getSelect2dZoneData(zoneId, organization);
console.log('response: ', response);
let res = await getFloatingZoneData(zoneId, organization); let res = await getFloatingZoneData(zoneId, organization);
setFloatingWidget(res); setFloatingWidget(res);
// Set the selected zone in the store
useDroppedObjectsStore.getState().setZone(zoneName, zoneId); useDroppedObjectsStore.getState().setZone(zoneName, zoneId);
if (Array.isArray(res)) { if (Array.isArray(res)) {
res.forEach((val) => { res.forEach((val) => {

View File

@ -5,6 +5,8 @@ import BarGraphComponent from "../realTimeVis/charts/BarGraphComponent";
import LineGraphComponent from "../realTimeVis/charts/LineGraphComponent"; import LineGraphComponent from "../realTimeVis/charts/LineGraphComponent";
import DoughnutGraphComponent from "../realTimeVis/charts/DoughnutGraphComponent"; import DoughnutGraphComponent from "../realTimeVis/charts/DoughnutGraphComponent";
import PolarAreaGraphComponent from "../realTimeVis/charts/PolarAreaGraphComponent"; import PolarAreaGraphComponent from "../realTimeVis/charts/PolarAreaGraphComponent";
import ProgressCard1 from "../realTimeVis/charts/ProgressCard1";
import ProgressCard2 from "../realTimeVis/charts/ProgressCard2";
import { import {
DeleteIcon, DeleteIcon,
DublicateIcon, DublicateIcon,
@ -13,6 +15,8 @@ import {
import { useEffect, useRef, useState } from "react"; import { useEffect, useRef, useState } from "react";
import { duplicateWidgetApi } from "../../../services/realTimeVisulization/zoneData/duplicateWidget"; import { duplicateWidgetApi } from "../../../services/realTimeVisulization/zoneData/duplicateWidget";
import { deleteWidgetApi } from "../../../services/realTimeVisulization/zoneData/deleteWidgetApi"; import { deleteWidgetApi } from "../../../services/realTimeVisulization/zoneData/deleteWidgetApi";
import { useClickOutside } from "./functions/handleWidgetsOuterClick";
import { useSocketStore } from "../../../store/store";
type Side = "top" | "bottom" | "left" | "right"; type Side = "top" | "bottom" | "left" | "right";
@ -69,6 +73,7 @@ export const DraggableWidget = ({
openKebabId: string | null; openKebabId: string | null;
setOpenKebabId: (id: string | null) => void; setOpenKebabId: (id: string | null) => void;
}) => { }) => {
const { visualizationSocket } = useSocketStore();
const { selectedChartId, setSelectedChartId } = useWidgetStore(); const { selectedChartId, setSelectedChartId } = useWidgetStore();
const [panelDimensions, setPanelDimensions] = useState<{ const [panelDimensions, setPanelDimensions] = useState<{
[side in Side]?: { width: number; height: number }; [side in Side]?: { width: number; height: number };
@ -79,30 +84,49 @@ export const DraggableWidget = ({
} }
}; };
const chartWidget = useRef<HTMLDivElement>(null);
const isPanelHidden = hiddenPanels.includes(widget.panel); const isPanelHidden = hiddenPanels.includes(widget.panel);
const deleteSelectedChart = async () => { const deleteSelectedChart = async () => {
try { try {
const email = localStorage.getItem("email") || ""; const email = localStorage.getItem("email") || "";
const organization = email?.split("@")[1]?.split(".")[0]; const organization = email?.split("@")[1]?.split(".")[0];
const response = await deleteWidgetApi(widget.id, organization); let deleteWidget = {
if (response?.message === "Widget deleted successfully") { zoneId: selectedZone.zoneId,
const updatedWidgets = selectedZone.widgets.filter( widgetID: widget.id,
(w: Widget) => w.id !== widget.id organization: organization
);
setSelectedZone((prevZone: any) => ({
...prevZone,
widgets: updatedWidgets,
}));
} }
} catch (error) { console.log('deleteWidget: ', deleteWidget);
if (visualizationSocket) {
visualizationSocket.emit("v2:viz-widget:delete", deleteWidget)
}
const updatedWidgets = selectedZone.widgets.filter(
(w: Widget) => w.id !== widget.id
);
console.log('updatedWidgets: ', updatedWidgets);
setSelectedZone((prevZone: any) => ({
...prevZone,
widgets: updatedWidgets,
}));
setOpenKebabId(null);
// const response = await deleteWidgetApi(widget.id, organization);
// if (response?.message === "Widget deleted successfully") {
// const updatedWidgets = selectedZone.widgets.filter(
// (w: Widget) => w.id !== widget.id
// );
// setSelectedZone((prevZone: any) => ({
// ...prevZone,
// widgets: updatedWidgets,
// }));
// }
} catch (error) {
} finally { } finally {
setOpenKebabId(null); setOpenKebabId(null);
} }
}; };
const getCurrentWidgetCount = (panel: Side) => const getCurrentWidgetCount = (panel: Side) =>
selectedZone.widgets.filter((w) => w.panel === panel).length; selectedZone.widgets.filter((w) => w.panel === panel).length;
@ -140,22 +164,33 @@ export const DraggableWidget = ({
id: `${widget.id}-copy-${Date.now()}`, id: `${widget.id}-copy-${Date.now()}`,
}; };
const response = await duplicateWidgetApi(selectedZone.zoneId, organization, duplicatedWidget); let duplicateWidget = {
organization: organization,
if (response?.message === "Widget created successfully") { zoneId: selectedZone.zoneId,
setSelectedZone((prevZone: any) => ({ widget: duplicatedWidget
...prevZone,
widgets: [...prevZone.widgets, duplicatedWidget],
}));
} }
} catch (error) { if (visualizationSocket) {
visualizationSocket.emit("v2:viz-widget:add", duplicateWidget)
}
setSelectedZone((prevZone: any) => ({
...prevZone,
widgets: [...prevZone.widgets, duplicatedWidget],
}));
// const response = await duplicateWidgetApi(selectedZone.zoneId, organization, duplicatedWidget);
// if (response?.message === "Widget created successfully") {
// setSelectedZone((prevZone: any) => ({
// ...prevZone,
// widgets: [...prevZone.widgets, duplicatedWidget],
// }));
// }
} catch (error) {
} finally { } finally {
setOpenKebabId(null); setOpenKebabId(null);
} }
}; };
const handleKebabClick = (event: React.MouseEvent<HTMLDivElement>) => { const handleKebabClick = (event: React.MouseEvent<HTMLDivElement>) => {
event.stopPropagation(); event.stopPropagation();
if (openKebabId === widget.id) { if (openKebabId === widget.id) {
@ -203,23 +238,31 @@ export const DraggableWidget = ({
onReorder(fromIndex, toIndex); // Call the reorder function passed as a prop 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 ( return (
<> <>
<div <div
draggable draggable
key={widget.id} key={widget.id}
className={`chart-container ${selectedChartId?.id === widget.id && "activeChart" className={`chart-container ${
}`} selectedChartId?.id === widget.id && "activeChart"
}`}
onPointerDown={handlePointerDown} onPointerDown={handlePointerDown}
onDragStart={handleDragStart} onDragStart={handleDragStart}
onDragEnter={handleDragEnter} onDragEnter={handleDragEnter}
onDragOver={handleDragOver} onDragOver={handleDragOver}
onDrop={handleDrop} onDrop={handleDrop}
style={{ style={{
opacity: isPanelHidden ? 0 : 1,
pointerEvents: isPanelHidden ? "none" : "auto", pointerEvents: isPanelHidden ? "none" : "auto",
}} }}
ref={chartWidget}
onClick={() => setSelectedChartId(widget)}
> >
{/* Kebab Icon */} {/* Kebab Icon */}
<div className="icon kebab" onClick={handleKebabClick}> <div className="icon kebab" onClick={handleKebabClick}>
@ -230,8 +273,9 @@ export const DraggableWidget = ({
{openKebabId === widget.id && ( {openKebabId === widget.id && (
<div className="kebab-options" ref={widgetRef}> <div className="kebab-options" ref={widgetRef}>
<div <div
className={`edit btn ${isPanelFull(widget.panel) ? "btn-blur" : "" className={`edit btn ${
}`} isPanelFull(widget.panel) ? "btn-blur" : ""
}`}
onClick={isPanelFull(widget.panel) ? undefined : duplicateWidget} onClick={isPanelFull(widget.panel) ? undefined : duplicateWidget}
> >
<div className="icon"> <div className="icon">
@ -249,9 +293,12 @@ export const DraggableWidget = ({
)} )}
{/* Render charts based on widget type */} {/* Render charts based on widget type */}
{widget.type === "progress" && ( {widget.type === "progress 1" && (
<ProgressCard title={widget.title} data={widget.data} /> <ProgressCard1 title={widget.title} id={widget.id} />
)}
{widget.type === "progress 2" && (
<ProgressCard2 title={widget.title} id={widget.id} />
)} )}
{widget.type === "line" && ( {widget.type === "line" && (
<LineGraphComponent <LineGraphComponent
@ -260,14 +307,6 @@ export const DraggableWidget = ({
title={widget.title} title={widget.title}
fontSize={widget.fontSize} fontSize={widget.fontSize}
fontWeight={widget.fontWeight} fontWeight={widget.fontWeight}
// data={{
// measurements: [
// { name: "testDevice", fields: "powerConsumption" },
// { name: "furnace", fields: "powerConsumption" },
// ],
// interval: 1000,
// duration: "1h",
// }}
/> />
)} )}
{widget.type === "bar" && ( {widget.type === "bar" && (
@ -277,14 +316,6 @@ export const DraggableWidget = ({
title={widget.title} title={widget.title}
fontSize={widget.fontSize} fontSize={widget.fontSize}
fontWeight={widget.fontWeight} fontWeight={widget.fontWeight}
// data={{
// measurements: [
// { name: "testDevice", fields: "powerConsumption" },
// { name: "furnace", fields: "powerConsumption" },
// ],
// interval: 1000,
// duration: "1h",
// }}
/> />
)} )}
{widget.type === "pie" && ( {widget.type === "pie" && (
@ -294,14 +325,6 @@ export const DraggableWidget = ({
title={widget.title} title={widget.title}
fontSize={widget.fontSize} fontSize={widget.fontSize}
fontWeight={widget.fontWeight} fontWeight={widget.fontWeight}
// data={{
// measurements: [
// { name: "testDevice", fields: "powerConsumption" },
// { name: "furnace", fields: "powerConsumption" },
// ],
// interval: 1000,
// duration: "1h",
// }}
/> />
)} )}
{widget.type === "doughnut" && ( {widget.type === "doughnut" && (

View File

@ -1,7 +1,6 @@
import { useThree } from "@react-three/fiber"; import { useThree } from "@react-three/fiber";
import React, { useState, useEffect } from "react"; import React, { useState, useEffect } from "react";
import { useAsset3dWidget, useWidgetSubOption } from "../../../store/store"; import { useAsset3dWidget, useSocketStore, useWidgetSubOption } from "../../../store/store";
import useModuleStore from "../../../store/useModuleStore"; import useModuleStore from "../../../store/useModuleStore";
import { ThreeState } from "../../../types/world/worldTypes"; import { ThreeState } from "../../../types/world/worldTypes";
import * as THREE from "three"; import * as THREE from "three";
@ -14,75 +13,60 @@ import { generateUniqueId } from "../../../functions/generateUniqueId";
import { adding3dWidgets } from "../../../services/realTimeVisulization/zoneData/add3dWidget"; import { adding3dWidgets } from "../../../services/realTimeVisulization/zoneData/add3dWidget";
import { get3dWidgetZoneData } from "../../../services/realTimeVisulization/zoneData/get3dWidgetData"; import { get3dWidgetZoneData } from "../../../services/realTimeVisulization/zoneData/get3dWidgetData";
import { use3DWidget } from "../../../store/useDroppedObjectsStore"; import { use3DWidget } from "../../../store/useDroppedObjectsStore";
import { useZoneWidgetStore } from "../../../store/useZone3DWidgetStore";
export default function Dropped3dWidgets() { export default function Dropped3dWidgets() {
const { widgetSelect } = useAsset3dWidget(); const { widgetSelect } = useAsset3dWidget();
const { activeModule } = useModuleStore(); const { activeModule } = useModuleStore();
const { raycaster, gl, scene }: ThreeState = useThree(); const { raycaster, gl, scene }: ThreeState = useThree();
const { widgetSubOption, setWidgetSubOption } = useWidgetSubOption(); const { widgetSubOption } = useWidgetSubOption();
const { selectedZone } = useSelectedZoneStore(); // Get the currently active zone const { selectedZone } = useSelectedZoneStore();
// 🔥 Store widget data (id, type, position) based on the selected zone
const [zoneWidgetData, setZoneWidgetData] = useState<
Record<string, { id: string; type: string; position: [number, number, number] }[]>
>({});
const { setWidgets3D } = use3DWidget()
useEffect(() => {
if (activeModule !== "visualization") return
if (selectedZone.zoneName === "") return;
const email = localStorage.getItem("email") || ""; // ✅ Use Zustand Store instead of useState
const organization = email?.split("@")[1]?.split(".")[0]; const { zoneWidgetData, setZoneWidgetData, addWidget } = useZoneWidgetStore();
const { setWidgets3D } = use3DWidget();
const { visualizationSocket } = useSocketStore();
useEffect(() => {
if (activeModule !== "visualization") return;
if (!selectedZone.zoneId) return;
const email = localStorage.getItem("email") || "";
const organization = email?.split("@")[1]?.split(".")[0];
async function get3dWidgetData() { async function get3dWidgetData() {
let result = await get3dWidgetZoneData(selectedZone.zoneId, organization); let result = await get3dWidgetZoneData(selectedZone.zoneId, organization);
setWidgets3D(result) console.log('result: ', result);
// Ensure the extracted data has id, type, and position correctly mapped setWidgets3D(result);
const formattedWidgets = result.map((widget: any) => ({ const formattedWidgets = result.map((widget: any) => ({
id: widget.id, id: widget.id,
type: widget.type, type: widget.type,
position: widget.position, position: widget.position,
})); }));
setZoneWidgetData((prev) => ({ setZoneWidgetData(selectedZone.zoneId, formattedWidgets);
...prev,
[selectedZone.zoneId]: formattedWidgets,
}));
} }
get3dWidgetData(); get3dWidgetData();
}, [selectedZone.zoneId, activeModule]);
}, [selectedZone.zoneId,activeModule]);
// useEffect(() => {
// // ✅ Set data only for the selected zone, keeping existing state structure
// setZoneWidgetData((prev) => ({
// ...prev,
// [selectedZone.zoneId]: [
// {
// "id": "1743322674626-50mucpb1c",
// "type": "ui-Widget 1",
// "position": [120.94655021768133, 4.142360029666558, 124.39283546121099]
// },
// {
// "id": "1743322682086-je2h9x33v",
// "type": "ui-Widget 2",
// "position": [131.28751045879255, 0.009999999999970264, 133.92059801984362]
// }
// ]
// }));
// }, [selectedZone.zoneId]); // ✅ Only update when the zone changes
useEffect(() => { useEffect(() => {
if (activeModule !== "visualization") return; if (activeModule !== "visualization") return;
if (widgetSubOption === "Floating") return; if (widgetSubOption === "Floating" || widgetSubOption === "2D") return;
if (selectedZone.zoneName === "") return if (selectedZone.zoneName === "") return;
const canvasElement = gl.domElement; const canvasElement = gl.domElement;
const onDrop = async (event: DragEvent) => { const onDrop = async (event: DragEvent) => {
event.preventDefault(); // Prevent default browser behavior event.preventDefault();
const email = localStorage.getItem("email") || ""; const email = localStorage.getItem("email") || "";
const organization = email?.split("@")[1]?.split(".")[0]; const organization = email?.split("@")[1]?.split(".")[0];
if (!widgetSelect.startsWith("ui")) return; if (!widgetSelect.startsWith("ui")) return;
const group1 = scene.getObjectByName("itemsGroup"); const group1 = scene.getObjectByName("itemsGroup");
if (!group1) return; if (!group1) return;
const intersects = raycaster.intersectObjects(scene.children, true).filter( const intersects = raycaster.intersectObjects(scene.children, true).filter(
(intersect) => (intersect) =>
!intersect.object.name.includes("Roof") && !intersect.object.name.includes("Roof") &&
@ -91,50 +75,55 @@ export default function Dropped3dWidgets() {
!intersect.object.userData.isPathObject && !intersect.object.userData.isPathObject &&
!(intersect.object.type === "GridHelper") !(intersect.object.type === "GridHelper")
); );
if (intersects.length > 0) { if (intersects.length > 0) {
const { x, y, z } = intersects[0].point; const { x, y, z } = intersects[0].point;
const newWidget = {
// ✅ Explicitly define position as a tuple
const newWidget: { id: string; type: string; position: [number, number, number] } = {
id: generateUniqueId(), id: generateUniqueId(),
type: widgetSelect, type: widgetSelect,
position: [x, y, z], // Ensures TypeScript recognizes it as a tuple position: [x, y, z] as [number, number, number],
}; };
let add3dWidget = {
organization: organization,
widget: newWidget,
zoneId: selectedZone.zoneId
}
console.log('add3dWidget: ', add3dWidget);
if (visualizationSocket) {
visualizationSocket.emit("v2:viz-3D-widget:add", add3dWidget)
}
let response = await adding3dWidgets(selectedZone.zoneId, organization, newWidget) // let response = await adding3dWidgets(selectedZone.zoneId, organization, newWidget);
// console.log('response: ', response);
// if (response.message === "Widget created successfully") {
// ✅ Store widgets uniquely for each zone addWidget(selectedZone.zoneId, newWidget);
setZoneWidgetData((prev) => ({ // }
...prev,
[selectedZone.zoneId]: [...(prev[selectedZone.zoneId] || []), newWidget],
}));
} }
}; };
canvasElement.addEventListener("drop", onDrop); canvasElement.addEventListener("drop", onDrop);
return () => { return () => {
canvasElement.removeEventListener("drop", onDrop); canvasElement.removeEventListener("drop", onDrop);
}; };
}, [widgetSelect, activeModule, selectedZone.zoneId, widgetSubOption]); }, [widgetSelect, activeModule, selectedZone.zoneId, widgetSubOption]);
// Get widgets for the currently active zone
const activeZoneWidgets = zoneWidgetData[selectedZone.zoneId] || []; const activeZoneWidgets = zoneWidgetData[selectedZone.zoneId] || [];
return ( return (
<> <>
{activeZoneWidgets.map(({ id, type, position }) => { {activeZoneWidgets.map(({ id, type, position }) => {
console.log('Typeeeeeeeeeeee',type);
switch (type) { switch (type) {
case "ui-Widget 1": case "ui-Widget 1":
return <ProductionCapacity key={id} position={position} />; return <ProductionCapacity key={id} id={id} type={type} position={position} />;
case "ui-Widget 2": case "ui-Widget 2":
return <ReturnOfInvestment key={id} position={position} />; return <ReturnOfInvestment key={id} id={id} type={type} position={position} />;
case "ui-Widget 3": case "ui-Widget 3":
return <StateWorking key={id} position={position} />; return <StateWorking key={id} id={id} type={type} position={position} />;
case "ui-Widget 4": case "ui-Widget 4":
return <Throughput key={id} position={position} />; return <Throughput key={id} id={id} type={type} position={position} />;
default: default:
return null; return null;
} }
@ -142,6 +131,3 @@ export default function Dropped3dWidgets() {
</> </>
); );
} }

View File

@ -1,4 +1,3 @@
import { WalletIcon } from "../../icons/3dChartIcons"; import { WalletIcon } from "../../icons/3dChartIcons";
import { useEffect, useRef, useState } from "react"; import { useEffect, useRef, useState } from "react";
import { import {
@ -21,6 +20,10 @@ import TotalCardComponent from "../realTimeVis/floating/TotalCardComponent";
import WarehouseThroughputComponent from "../realTimeVis/floating/WarehouseThroughputComponent"; import WarehouseThroughputComponent from "../realTimeVis/floating/WarehouseThroughputComponent";
import FleetEfficiencyComponent from "../realTimeVis/floating/FleetEfficiencyComponent"; import FleetEfficiencyComponent from "../realTimeVis/floating/FleetEfficiencyComponent";
import { useWidgetStore } from "../../../store/useWidgetStore"; 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 { interface DraggingState {
zone: string; zone: string;
index: number; index: number;
@ -43,19 +46,25 @@ interface DraggingState {
}; };
} }
const DroppedObjects: React.FC = () => { const DroppedObjects: React.FC = () => {
const { visualizationSocket } = useSocketStore();
const { isPlaying } = usePlayButtonStore();
const zones = useDroppedObjectsStore((state) => state.zones); const zones = useDroppedObjectsStore((state) => state.zones);
const [openKebabId, setOpenKebabId] = useState<string | null>(null); const [openKebabId, setOpenKebabId] = useState<string | null>(null);
const updateObjectPosition = useDroppedObjectsStore( const updateObjectPosition = useDroppedObjectsStore(
(state) => state.updateObjectPosition (state) => state.updateObjectPosition
); );
const { setSelectedZone, selectedZone } = useSelectedZoneStore();
const deleteObject = useDroppedObjectsStore((state) => state.deleteObject); const deleteObject = useDroppedObjectsStore((state) => state.deleteObject);
const duplicateObject = useDroppedObjectsStore((state) => state.duplicateObject); const duplicateObject = useDroppedObjectsStore(
(state) => state.duplicateObject
);
const [draggingIndex, setDraggingIndex] = useState<DraggingState | null>( const [draggingIndex, setDraggingIndex] = useState<DraggingState | null>(
null null
); );
const [offset, setOffset] = useState<[number, number] | null>(null); const [offset, setOffset] = useState<[number, number] | null>(null);
const { setSelectedChartId } = useWidgetStore(); const { selectedChartId, setSelectedChartId } = useWidgetStore();
const [activeEdges, setActiveEdges] = useState<{ const [activeEdges, setActiveEdges] = useState<{
vertical: "top" | "bottom"; vertical: "top" | "bottom";
horizontal: "left" | "right"; horizontal: "left" | "right";
@ -68,6 +77,13 @@ const DroppedObjects: React.FC = () => {
} | null>(null); // State to track the current position during drag } | null>(null); // State to track the current position during drag
const animationRef = useRef<number | null>(null); const animationRef = useRef<number | null>(null);
const { activeModule } = useModuleStore(); const { activeModule } = useModuleStore();
const chartWidget = useRef<HTMLDivElement>(null);
// useClickOutside(chartWidget, () => {
// setSelectedChartId(null);
// });
const kebabRef = useRef<HTMLDivElement>(null);
// Clean up animation frame on unmount // Clean up animation frame on unmount
useEffect(() => { useEffect(() => {
@ -77,35 +93,67 @@ const DroppedObjects: React.FC = () => {
} }
}; };
}, []); }, []);
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
if (kebabRef.current && !kebabRef.current.contains(event.target as Node)) {
setOpenKebabId(null);
}
};
// Add event listener when component mounts
document.addEventListener("mousedown", handleClickOutside);
// Clean up event listener when component unmounts
return () => {
document.removeEventListener("mousedown", handleClickOutside);
};
}, []);
const zoneEntries = Object.entries(zones); const zoneEntries = Object.entries(zones);
if (zoneEntries.length === 0) return null; if (zoneEntries.length === 0) return null;
const [zoneName, zone] = zoneEntries[0]; const [zoneName, zone] = zoneEntries[0];
function handleDuplicate(zoneName: string, index: number) { function handleDuplicate(zoneName: string, index: number) {
setOpenKebabId(null) setOpenKebabId(null);
duplicateObject(zoneName, index); // Call the duplicateObject method from the store duplicateObject(zoneName, index); // Call the duplicateObject method from the store
} }
async function handleDelete(zoneName: string, id: string, index: number) { async function handleDelete(zoneName: string, id: string) {
try { try {
const email = localStorage.getItem("email") || ""; const email = localStorage.getItem("email") || "";
const organization = email?.split("@")[1]?.split(".")[0]; const organization = email?.split("@")[1]?.split(".")[0];
let res = await deleteFloatingWidgetApi(id, organization);
console.log('res: ', res); let deleteFloatingWidget = {
floatWidgetID: id,
if (res.message === "FloatingWidget deleted successfully") { organization: organization,
deleteObject(zoneName, index); // Call the deleteObject method from the store zoneId: zone.zoneId
} }
if (visualizationSocket) {
visualizationSocket.emit("v2:viz-float:delete", deleteFloatingWidget)
}
deleteObject(zoneName, id);
// let res = await deleteFloatingWidgetApi(id, organization);
// s
// if (res.message === "FloatingWidget deleted successfully") {
// deleteObject(zoneName, id, index); // Call the deleteObject method from the store
// }
} catch (error) { } catch (error) {
console.error("Error deleting floating widget:", error);
} }
} }
const handlePointerDown = (event: React.PointerEvent, index: number) => { const handlePointerDown = (event: React.PointerEvent, index: number) => {
if ((event.target as HTMLElement).closest(".kebab-options") || (event.target as HTMLElement).closest(".kebab")) {
return; // Prevent dragging when clicking on the kebab menu or its options
}
const obj = zone.objects[index]; const obj = zone.objects[index];
const element = event.currentTarget as HTMLElement;
element.setPointerCapture(event.pointerId);
const container = document.getElementById("real-time-vis-canvas"); const container = document.getElementById("real-time-vis-canvas");
if (!container) return; if (!container) return;
@ -145,11 +193,150 @@ const DroppedObjects: React.FC = () => {
} }
setOffset([offsetY, offsetX]); 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) => { const handlePointerMove = (event: React.PointerEvent) => {
if (!draggingIndex || !offset) return; if (!draggingIndex || !offset) return;
if (isPlaying === true) return;
const container = document.getElementById("real-time-vis-canvas"); const container = document.getElementById("real-time-vis-canvas");
if (!container) return; if (!container) return;
@ -198,18 +385,21 @@ const DroppedObjects: React.FC = () => {
// Update the current position state for DistanceLines // Update the current position state for DistanceLines
setCurrentPosition(newPosition); setCurrentPosition(newPosition);
// Update position immediately without animation frame
updateObjectPosition(zoneName, draggingIndex.index, newPosition);
if (!animationRef.current) { // if (!animationRef.current) {
animationRef.current = requestAnimationFrame(() => { // animationRef.current = requestAnimationFrame(() => {
updateObjectPosition(zoneName, draggingIndex.index, newPosition); // updateObjectPosition(zoneName, draggingIndex.index, newPosition);
animationRef.current = null; // animationRef.current = null;
}); // });
} // }
}; };
const handlePointerUp = async (event: React.PointerEvent<HTMLDivElement>) => { const handlePointerUp = async (event: React.PointerEvent<HTMLDivElement>) => {
try { try {
if (!draggingIndex || !offset) return; if (!draggingIndex || !offset) return;
if (isPlaying === true) return;
const container = document.getElementById("real-time-vis-canvas"); const container = document.getElementById("real-time-vis-canvas");
if (!container) return; if (!container) return;
@ -218,11 +408,11 @@ const DroppedObjects: React.FC = () => {
const relativeX = event.clientX - rect.left; const relativeX = event.clientX - rect.left;
const relativeY = event.clientY - rect.top; const relativeY = event.clientY - rect.top;
// Only now determine the final position strategy // Determine final position strategy
const finalPosition = determinePosition(rect, relativeX, relativeY); const finalPosition = determinePosition(rect, relativeX, relativeY);
const [activeProp1, activeProp2] = getActiveProperties(finalPosition); const [activeProp1, activeProp2] = getActiveProperties(finalPosition);
// Calculate final position using the new strategy // Calculate final position
let finalY = 0; let finalY = 0;
let finalX = 0; let finalX = 0;
@ -246,31 +436,62 @@ const DroppedObjects: React.FC = () => {
...finalPosition, ...finalPosition,
[activeProp1]: finalY, [activeProp1]: finalY,
[activeProp2]: finalX, [activeProp2]: finalX,
// Clear opposite properties
[activeProp1 === "top" ? "bottom" : "top"]: "auto",
[activeProp2 === "left" ? "right" : "left"]: "auto",
}; };
// Save to backend // Save to backend
const email = localStorage.getItem("email") || ""; const email = localStorage.getItem("email") || "";
const organization = email?.split("@")[1]?.split(".")[0]; const organization = email?.split("@")[1]?.split(".")[0];
const response = await addingFloatingWidgets(zone.zoneId, organization, { // const response = await addingFloatingWidgets(zone.zoneId, organization, {
...zone.objects[draggingIndex.index], // ...zone.objects[draggingIndex.index],
position: boundedPosition, // position: boundedPosition,
}); // });
if (response.message === "Widget updated successfully") { let updateFloatingWidget = {
updateObjectPosition(zoneName, draggingIndex.index, boundedPosition); organization: organization,
widget: {
...zone.objects[draggingIndex.index],
position: boundedPosition,
},
index:draggingIndex.index,
zoneId: zone.zoneId
}
if (visualizationSocket) {
visualizationSocket.emit("v2:viz-float:add", updateFloatingWidget)
} }
// Clean up // if (response.message === "Widget updated successfully") {
console.log('boundedPosition: ', boundedPosition);
updateObjectPosition(zoneName, draggingIndex.index, boundedPosition);
// }
// // Clean up
// setDraggingIndex(null);
// setOffset(null);
// setActiveEdges(null); // Clear active edges
// setCurrentPosition(null); // Reset current position
// if (animationRef.current) {
// cancelAnimationFrame(animationRef.current);
// animationRef.current = null;
// }
} catch (error) {
} finally {
// Clean up regardless of success or failure
setDraggingIndex(null); setDraggingIndex(null);
setOffset(null); setOffset(null);
setActiveEdges(null); // Clear active edges setActiveEdges(null);
setCurrentPosition(null); // Reset current position setCurrentPosition(null);
// Cancel any pending animation frame
if (animationRef.current) { if (animationRef.current) {
cancelAnimationFrame(animationRef.current); cancelAnimationFrame(animationRef.current);
animationRef.current = null; animationRef.current = null;
} }
} catch (error) {
console.error("Error in handlePointerUp:", error);
} }
}; };
@ -279,66 +500,6 @@ const DroppedObjects: React.FC = () => {
setOpenKebabId((prevId) => (prevId === id ? null : id)); setOpenKebabId((prevId) => (prevId === id ? null : id));
}; };
const renderObjectContent = (obj: any) => {
switch (obj.className) {
case "floating total-card":
return (
<>
<div className="header-wrapper">
<div className="header">{obj.header}</div>
<div className="data-values">
<div className="value">{obj.value}</div>
<div className="per">{obj.per}</div>
</div>
</div>
<div className="icon">
<WalletIcon />
</div>
</>
);
case "warehouseThroughput floating":
return (
<>
<div className="header">
<h2>Warehouse Throughput</h2>
<p>
<span>(+5) more</span> in 2025
</p>
</div>
<div className="lineGraph" style={{ height: "100%" }}>
{/* <Line data={lineGraphData} options={lineGraphOptions} /> */}
</div>
</>
);
case "fleetEfficiency floating":
return (
<>
<h2 className="header">Fleet Efficiency</h2>
<div className="progressContainer">
<div className="progress">
<div className="barOverflow">
<div
className="bar"
style={{ transform: `rotate(${obj.value}deg)` }}
></div>
</div>
</div>
</div>
<div className="scaleLabels">
<span>0%</span>
<div className="centerText">
<div className="percentage">{obj.per}%</div>
<div className="status">Optimal</div>
</div>
<span>100%</span>
</div>
</>
);
default:
return null;
}
};
return ( return (
<div <div
onPointerMove={handlePointerMove} onPointerMove={handlePointerMove}
@ -348,29 +509,48 @@ const DroppedObjects: React.FC = () => {
{zone.objects.map((obj, index) => ( {zone.objects.map((obj, index) => (
<div <div
key={`${zoneName}-${index}`} key={`${zoneName}-${index}`}
className={obj.className} className={`${obj.className} ${
selectedChartId?.id === obj.id && "activeChart"
}`}
ref={chartWidget}
style={{ style={{
position: "absolute", position: "absolute",
top: top:
typeof obj.position.top === "number" typeof obj.position.top === "number"
? `${obj.position.top}px` ? `calc(${obj.position.top}px + ${
isPlaying && selectedZone.activeSides.includes("top")
? 90
: 0
}px)`
: "auto", : "auto",
left: left:
typeof obj.position.left === "number" typeof obj.position.left === "number"
? `${obj.position.left}px` ? `calc(${obj.position.left}px + ${
isPlaying && selectedZone.activeSides.includes("left")
? 90
: 0
}px)`
: "auto", : "auto",
right: right:
typeof obj.position.right === "number" typeof obj.position.right === "number"
? `${obj.position.right}px` ? `calc(${obj.position.right}px + ${
isPlaying && selectedZone.activeSides.includes("right")
? 90
: 0
}px)`
: "auto", : "auto",
bottom: bottom:
typeof obj.position.bottom === "number" typeof obj.position.bottom === "number"
? `${obj.position.bottom}px` ? `calc(${obj.position.bottom}px + ${
isPlaying && selectedZone.activeSides.includes("bottom")
? 90
: 0
}px)`
: "auto", : "auto",
}} }}
onPointerDown={(event) => handlePointerDown(event, index)} onPointerDown={(event) => {
onClick={() => { setSelectedChartId(obj);
setSelectedChartId(obj) handlePointerDown(event, index);
}} }}
> >
{obj.className === "floating total-card" ? ( {obj.className === "floating total-card" ? (
@ -379,7 +559,7 @@ const DroppedObjects: React.FC = () => {
</> </>
) : obj.className === "warehouseThroughput floating" ? ( ) : obj.className === "warehouseThroughput floating" ? (
<> <>
<WarehouseThroughputComponent /> <WarehouseThroughputComponent object={obj}/>
</> </>
) : obj.className === "fleetEfficiency floating" ? ( ) : obj.className === "fleetEfficiency floating" ? (
<> <>
@ -388,12 +568,16 @@ const DroppedObjects: React.FC = () => {
) : null} ) : null}
<div <div
className="icon kebab" className="icon kebab"
onClick={(event) => handleKebabClick(obj.id, event)} ref={kebabRef}
onClick={(event) => {
event.stopPropagation();
handleKebabClick(obj.id, event)
}}
> >
<KebabIcon /> <KebabIcon />
</div> </div>
{openKebabId === obj.id && ( {openKebabId === obj.id && (
<div className="kebab-options"> <div className="kebab-options" ref={kebabRef}>
<div className="dublicate btn" onClick={(event) => { <div className="dublicate btn" onClick={(event) => {
event.stopPropagation(); event.stopPropagation();
handleDuplicate(zoneName, index); // Call the duplicate handler handleDuplicate(zoneName, index); // Call the duplicate handler
@ -405,7 +589,7 @@ const DroppedObjects: React.FC = () => {
</div> </div>
<div className="edit btn" onClick={(event) => { <div className="edit btn" onClick={(event) => {
event.stopPropagation(); event.stopPropagation();
handleDelete(zoneName, obj.id, index); // Call the delete handler handleDelete(zoneName, obj.id); // Call the delete handler
}}> }}>
<div className="icon"> <div className="icon">
<DeleteIcon /> <DeleteIcon />
@ -414,11 +598,13 @@ const DroppedObjects: React.FC = () => {
</div> </div>
</div> </div>
)} )}
</div> </div>
))} ))}
{/* Render DistanceLines component during drag */} {/* Render DistanceLines component during drag */}
{draggingIndex !== null && {isPlaying === false &&
draggingIndex !== null &&
activeEdges !== null && activeEdges !== null &&
currentPosition !== null && ( currentPosition !== null && (
<DistanceLines <DistanceLines

View File

@ -4,6 +4,7 @@ import { usePlayButtonStore } from "../../../store/usePlayButtonStore";
import { DraggableWidget } from "./DraggableWidget"; import { DraggableWidget } from "./DraggableWidget";
import { arrayMove } from "@dnd-kit/sortable"; import { arrayMove } from "@dnd-kit/sortable";
import { addingWidgets } from "../../../services/realTimeVisulization/zoneData/addWidgets"; import { addingWidgets } from "../../../services/realTimeVisulization/zoneData/addWidgets";
import { useAsset3dWidget, useSocketStore } from "../../../store/store";
type Side = "top" | "bottom" | "left" | "right"; type Side = "top" | "bottom" | "left" | "right";
@ -24,7 +25,7 @@ interface PanelProps {
lockedPanels: Side[]; lockedPanels: Side[];
zoneId: string; zoneId: string;
zoneViewPortTarget: number[]; zoneViewPortTarget: number[];
zoneViewPortPosition: number[] zoneViewPortPosition: number[];
widgets: Widget[]; widgets: Widget[];
}; };
setSelectedZone: React.Dispatch< setSelectedZone: React.Dispatch<
@ -36,7 +37,7 @@ interface PanelProps {
lockedPanels: Side[]; lockedPanels: Side[];
zoneId: string; zoneId: string;
zoneViewPortTarget: number[]; zoneViewPortTarget: number[];
zoneViewPortPosition: number[] zoneViewPortPosition: number[];
widgets: Widget[]; widgets: Widget[];
}> }>
>; >;
@ -53,6 +54,7 @@ const Panel: React.FC<PanelProps> = ({
hiddenPanels, hiddenPanels,
setZonesData, setZonesData,
}) => { }) => {
const { widgetSelect, setWidgetSelect } = useAsset3dWidget();
const panelRefs = useRef<{ [side in Side]?: HTMLDivElement }>({}); const panelRefs = useRef<{ [side in Side]?: HTMLDivElement }>({});
const [panelDimensions, setPanelDimensions] = useState<{ const [panelDimensions, setPanelDimensions] = useState<{
[side in Side]?: { width: number; height: number }; [side in Side]?: { width: number; height: number };
@ -60,6 +62,7 @@ const Panel: React.FC<PanelProps> = ({
const [openKebabId, setOpenKebabId] = useState<string | null>(null); const [openKebabId, setOpenKebabId] = useState<string | null>(null);
const { isPlaying } = usePlayButtonStore(); const { isPlaying } = usePlayButtonStore();
const { visualizationSocket } = useSocketStore();
const getPanelStyle = useMemo( const getPanelStyle = useMemo(
() => (side: Side) => { () => (side: Side) => {
@ -75,8 +78,9 @@ const Panel: React.FC<PanelProps> = ({
case "top": case "top":
case "bottom": case "bottom":
return { return {
width: `calc(100% - ${(leftActive ? panelSize : 0) + (rightActive ? panelSize : 0) width: `calc(100% - ${
}px)`, (leftActive ? panelSize : 0) + (rightActive ? panelSize : 0)
}px)`,
height: `${panelSize - 2}px`, height: `${panelSize - 2}px`,
left: leftActive ? `${panelSize}px` : "0", left: leftActive ? `${panelSize}px` : "0",
right: rightActive ? `${panelSize}px` : "0", right: rightActive ? `${panelSize}px` : "0",
@ -86,8 +90,9 @@ const Panel: React.FC<PanelProps> = ({
case "right": case "right":
return { return {
width: `${panelSize - 2}px`, width: `${panelSize - 2}px`,
height: `calc(100% - ${(topActive ? panelSize : 0) + (bottomActive ? panelSize : 0) height: `calc(100% - ${
}px)`, (topActive ? panelSize : 0) + (bottomActive ? panelSize : 0)
}px)`,
top: topActive ? `${panelSize}px` : "0", top: topActive ? `${panelSize}px` : "0",
bottom: bottomActive ? `${panelSize}px` : "0", bottom: bottomActive ? `${panelSize}px` : "0",
[side]: "0", [side]: "0",
@ -100,7 +105,6 @@ const Panel: React.FC<PanelProps> = ({
); );
const handleDrop = (e: React.DragEvent, panel: Side) => { const handleDrop = (e: React.DragEvent, panel: Side) => {
e.preventDefault(); e.preventDefault();
const { draggedAsset } = useWidgetStore.getState(); const { draggedAsset } = useWidgetStore.getState();
if (!draggedAsset) return; if (!draggedAsset) return;
@ -110,7 +114,6 @@ const Panel: React.FC<PanelProps> = ({
const maxCapacity = calculatePanelCapacity(panel); const maxCapacity = calculatePanelCapacity(panel);
if (currentWidgetsCount >= maxCapacity) return; if (currentWidgetsCount >= maxCapacity) return;
addWidgetToPanel(draggedAsset, panel); addWidgetToPanel(draggedAsset, panel);
}; };
@ -141,21 +144,34 @@ const Panel: React.FC<PanelProps> = ({
// while dublicate check this and add // while dublicate check this and add
const addWidgetToPanel = async (asset: any, panel: Side) => { const addWidgetToPanel = async (asset: any, panel: Side) => {
const email = localStorage.getItem("email") || ""; const email = localStorage.getItem("email") || "";
const organization = email?.split("@")[1]?.split(".")[0] const organization = email?.split("@")[1]?.split(".")[0];
const newWidget = { const newWidget = {
...asset, ...asset,
id: generateUniqueId(), id: generateUniqueId(),
panel, panel,
}; };
let addWidget = {
organization: organization,
zoneId: selectedZone.zoneId,
widget: newWidget
}
if (visualizationSocket) {
visualizationSocket.emit("v2:viz-widget:add", addWidget)
}
setSelectedZone((prev) => ({
...prev,
widgets: [...prev.widgets, newWidget],
}));
try { try {
let response = await addingWidgets(selectedZone.zoneId, organization, newWidget); // let response = await addingWidgets(selectedZone.zoneId, organization, newWidget);
if (response.message === "Widget created successfully") { // if (response.message === "Widget created successfully") {
setSelectedZone((prev) => ({ // setSelectedZone((prev) => ({
...prev, // ...prev,
widgets: [...prev.widgets, newWidget], // widgets: [...prev.widgets, newWidget],
})); // }));
} // }
} catch (error) { } catch (error) {
console.error("Error adding widget:", error); console.error("Error adding widget:", error);
} }
@ -191,7 +207,6 @@ const Panel: React.FC<PanelProps> = ({
const handleReorder = (fromIndex: number, toIndex: number, panel: Side) => { const handleReorder = (fromIndex: number, toIndex: number, panel: Side) => {
if (!selectedZone) return; // Ensure selectedZone is not null if (!selectedZone) return; // Ensure selectedZone is not null
setSelectedZone((prev) => { setSelectedZone((prev) => {
if (!prev) return prev; // Ensure prev is not null if (!prev) return prev; // Ensure prev is not null
@ -218,7 +233,9 @@ const Panel: React.FC<PanelProps> = ({
{selectedZone.activeSides.map((side) => ( {selectedZone.activeSides.map((side) => (
<div <div
key={side} key={side}
className={`panel ${side}-panel absolute ${isPlaying && ""}`} className={`panel ${side}-panel absolute ${isPlaying ? "" : ""} ${
hiddenPanels.includes(side) ? "hidePanel" : ""
}`}
style={getPanelStyle(side)} style={getPanelStyle(side)}
onDrop={(e) => handleDrop(e, side)} onDrop={(e) => handleDrop(e, side)}
onDragOver={(e) => e.preventDefault()} onDragOver={(e) => e.preventDefault()}

View File

@ -7,10 +7,11 @@ import DisplayZone from "./DisplayZone";
import Scene from "../../../modules/scene/scene"; import Scene from "../../../modules/scene/scene";
import useModuleStore from "../../../store/useModuleStore"; import useModuleStore from "../../../store/useModuleStore";
import DroppedObjects from "./DroppedFloatingWidgets";
import { useDroppedObjectsStore } from "../../../store/useDroppedObjectsStore"; import { useDroppedObjectsStore } from "../../../store/useDroppedObjectsStore";
import { import {
useAsset3dWidget, useAsset3dWidget,
useSocketStore,
useWidgetSubOption, useWidgetSubOption,
useZones, useZones,
} from "../../../store/store"; } from "../../../store/store";
@ -18,6 +19,10 @@ import { getZone2dData } from "../../../services/realTimeVisulization/zoneData/g
import { generateUniqueId } from "../../../functions/generateUniqueId"; import { generateUniqueId } from "../../../functions/generateUniqueId";
import { determinePosition } from "./functions/determinePosition"; import { determinePosition } from "./functions/determinePosition";
import { addingFloatingWidgets } from "../../../services/realTimeVisulization/zoneData/addFloatingWidgets"; 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"; type Side = "top" | "bottom" | "left" | "right";
@ -51,11 +56,15 @@ const RealTimeVisulization: React.FC = () => {
const [zonesData, setZonesData] = useState<FormattedZoneData>({}); const [zonesData, setZonesData] = useState<FormattedZoneData>({});
const { selectedZone, setSelectedZone } = useSelectedZoneStore(); const { selectedZone, setSelectedZone } = useSelectedZoneStore();
const { zones } = useZones(); const { zones } = useZones();
const [openConfirmationPopup, setOpenConfirmationPopup] = useState(false);
const [floatingWidgets, setFloatingWidgets] = useState< const [floatingWidgets, setFloatingWidgets] = useState<
Record<string, { zoneName: string; zoneId: string; objects: any[] }> Record<string, { zoneName: string; zoneId: string; objects: any[] }>
>({}); >({});
const { widgetSelect, setWidgetSelect } = useAsset3dWidget(); const { widgetSelect, setWidgetSelect } = useAsset3dWidget();
const { widgetSubOption, setWidgetSubOption } = useWidgetSubOption(); const { widgetSubOption, setWidgetSubOption } = useWidgetSubOption();
const { visualizationSocket } = useSocketStore();
useEffect(() => { useEffect(() => {
async function GetZoneData() { async function GetZoneData() {
@ -84,9 +93,7 @@ const RealTimeVisulization: React.FC = () => {
{} {}
); );
setZonesData(formattedData); setZonesData(formattedData);
} catch (error) { } catch (error) {}
}
} }
GetZoneData(); GetZoneData();
@ -132,18 +139,13 @@ const RealTimeVisulization: React.FC = () => {
const relativeX = event.clientX - canvasRect.left; const relativeX = event.clientX - canvasRect.left;
const relativeY = event.clientY - canvasRect.top; const relativeY = event.clientY - canvasRect.top;
const newPosition = determinePosition(canvasRect, relativeX, relativeY)
console.log('newPosition: ', newPosition);
const newObject = { const newObject = {
...droppedData, ...droppedData,
id: generateUniqueId(), id: generateUniqueId(),
position: determinePosition(canvasRect, relativeX, relativeY), position: determinePosition(canvasRect, relativeX, relativeY),
}; };
console.log('newObject: ', newObject);
let response = await addingFloatingWidgets(
selectedZone.zoneId,
organization,
newObject
);
// Only set zone if its not already in the store (prevents overwriting objects) // Only set zone if its not already in the store (prevents overwriting objects)
const existingZone = const existingZone =
@ -154,12 +156,28 @@ const RealTimeVisulization: React.FC = () => {
.setZone(selectedZone.zoneName, selectedZone.zoneId); .setZone(selectedZone.zoneName, selectedZone.zoneId);
} }
// Add the dropped object to the zone if the API call is successful let addFloatingWidget = {
if (response.message === "FloatWidget created successfully") { organization: organization,
useDroppedObjectsStore widget: newObject,
.getState() zoneId: selectedZone.zoneId
.addObject(selectedZone.zoneName, newObject);
} }
console.log('newObject: ', newObject);
if (visualizationSocket) {
visualizationSocket.emit("v2:viz-float:add", addFloatingWidget)
}
// let response = await addingFloatingWidgets(
// selectedZone.zoneId,
// organization,
// newObject
// );
// Add the dropped object to the zone if the API call is successful
// if (response.message === "FloatWidget created successfully") {
useDroppedObjectsStore
.getState()
.addObject(selectedZone.zoneName, newObject);
// }
// Update floating widgets state // Update floating widgets state
setFloatingWidgets((prevWidgets) => ({ setFloatingWidgets((prevWidgets) => ({
@ -174,7 +192,7 @@ const RealTimeVisulization: React.FC = () => {
], ],
}, },
})); }));
} catch (error) { } } catch (error) {}
}; };
return ( return (
@ -188,6 +206,20 @@ const RealTimeVisulization: React.FC = () => {
left: isPlaying || activeModule !== "visualization" ? "0%" : "", 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 <div
className="scene-container" className="scene-container"
style={{ style={{
@ -202,7 +234,13 @@ const RealTimeVisulization: React.FC = () => {
<Scene /> <Scene />
</div> </div>
{activeModule === "visualization" && selectedZone.zoneName !== "" && <DroppedObjects />} {activeModule === "visualization" && selectedZone.zoneName !== "" && <DroppedObjects />}
{activeModule === "visualization" && <SocketRealTimeViz />}
{/* <DroppedObjects /> */} {/* <DroppedObjects /> */}
{/* <RenderOverlay>
<EditWidgetOption
options={["Dublicate", "Vertical Move", "Horizontal Move", "Delete"]}
/>
</RenderOverlay> */}
{activeModule === "visualization" && ( {activeModule === "visualization" && (
<> <>
<DisplayZone <DisplayZone

View File

@ -1,69 +1,3 @@
// export function determinePosition(
// canvasRect: DOMRect,
// relativeX: number,
// relativeY: number
// ): {
// top: number | "auto";
// left: number | "auto";
// right: number | "auto";
// bottom: number | "auto";
// } {
// // Calculate the midpoints of the canvas
// const centerX = canvasRect.width / 2;
// const centerY = canvasRect.height / 2;
// // Initialize position with default values
// let position: {
// top: number | "auto";
// left: number | "auto";
// right: number | "auto";
// bottom: number | "auto";
// };
// if (relativeY < centerY) {
// // Top half
// if (relativeX < centerX) {
// // Left side
// position = {
// top: relativeY,
// left: relativeX,
// right: "auto",
// bottom: "auto",
// };
// } else {
// // Right side
// position = {
// top: relativeY,
// right: canvasRect.width - relativeX,
// left: "auto",
// bottom: "auto",
// };
// }
// } else {
// // Bottom half
// if (relativeX < centerX) {
// // Left side
// position = {
// bottom: canvasRect.height - relativeY,
// left: relativeX,
// right: "auto",
// top: "auto",
// };
// } else {
// // Right side
// position = {
// bottom: canvasRect.height - relativeY,
// right: canvasRect.width - relativeX,
// left: "auto",
// top: "auto",
// };
// }
// }
// return position;
// }
export function determinePosition( export function determinePosition(
canvasRect: DOMRect, canvasRect: DOMRect,
@ -87,36 +21,36 @@ export function determinePosition(
if (relativeY < centerY) { if (relativeY < centerY) {
if (relativeX < centerX) { if (relativeX < centerX) {
console.log("Top-left");
position = { position = {
top: relativeY, top: relativeY - 41.5,
left: relativeX, left: relativeX - 125,
right: "auto", right: "auto",
bottom: "auto", bottom: "auto",
}; };
} else { } else {
console.log("Top-right");
position = { position = {
top: relativeY, top: relativeY - 41.5,
right: canvasRect.width - relativeX, right: canvasRect.width - relativeX - 125,
left: "auto", left: "auto",
bottom: "auto", bottom: "auto",
}; };
} }
} else { } else {
if (relativeX < centerX) { if (relativeX < centerX) {
console.log("Bottom-left");
position = { position = {
bottom: canvasRect.height - relativeY, bottom: canvasRect.height - relativeY - 41.5,
left: relativeX, left: relativeX - 125,
right: "auto", right: "auto",
top: "auto", top: "auto",
}; };
} else { } else {
console.log("Bottom-right");
position = { position = {
bottom: canvasRect.height - relativeY, bottom: canvasRect.height - relativeY - 41.5,
right: canvasRect.width - relativeX, right: canvasRect.width - relativeX - 125,
left: "auto", left: "auto",
top: "auto", top: "auto",
}; };
@ -124,4 +58,4 @@ export function determinePosition(
} }
return position; return position;
} }

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 onChange?: (value: number) => void; // Function to handle toggle clicks
disabled?: boolean; disabled?: boolean;
value?: number; value?: number;
onPointerUp?: (value: number) => void;
} }
const InputRange: React.FC<InputToggleProps> = ({ const InputRange: React.FC<InputToggleProps> = ({
@ -17,9 +18,10 @@ const InputRange: React.FC<InputToggleProps> = ({
min, min,
max, max,
disabled, 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>) { function handleChange(e: React.ChangeEvent<HTMLInputElement>) {
const newValue = parseInt(e.target.value); // Parse the value to an integer const newValue = parseInt(e.target.value); // Parse the value to an integer
@ -31,8 +33,22 @@ const InputRange: React.FC<InputToggleProps> = ({
} }
} }
useEffect(() => { useEffect(() => {
setRangeValue(value); value && setRangeValue(value);
}, [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 ( return (
<div className="input-range-container"> <div className="input-range-container">
@ -52,6 +68,7 @@ const InputRange: React.FC<InputToggleProps> = ({
onChange={handleChange} onChange={handleChange}
disabled={disabled} disabled={disabled}
value={rangeValue} value={rangeValue}
onPointerUp={handlePointerUp}
/> />
<input <input
type="number" type="number"
@ -61,6 +78,12 @@ const InputRange: React.FC<InputToggleProps> = ({
value={rangeValue} value={rangeValue}
onChange={handleChange} onChange={handleChange}
disabled={disabled} disabled={disabled}
onKeyUp={(e) => {
if (e.key === "ArrowUp" || e.key === "ArrowDown") {
console.log("e.key: ", e.key);
handlekey(e);
}
}}
/> />
</div> </div>
</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 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 // 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 handleDragStart = (event: React.DragEvent<HTMLDivElement>) => {
const rect = event.currentTarget.getBoundingClientRect(); // Get position 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 = ({ const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL;
object
}: any) => { // 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 ( return (
<> <>
<h2 className="header">Fleet Efficiency</h2> <h2 className="header">{name}</h2>
<div className="progressContainer"> <div className="progressContainer">
<div className="progress"> <div className="progress">
<div className="barOverflow"> <div className="barOverflow">
<div <div
className="bar" className="bar"
style={{ transform: `rotate(${object.value}deg)` }} style={{ transform: `rotate(${rotationAngle}deg)` }}
></div> ></div>
</div> </div>
</div> </div>
@ -21,7 +101,7 @@ const FleetEfficiencyComponent = ({
<div className="scaleLabels"> <div className="scaleLabels">
<span>0%</span> <span>0%</span>
<div className="centerText"> <div className="centerText">
<div className="percentage">{object.per}%</div> <div className="percentage">{progress}%</div>
<div className="status">Optimal</div> <div className="status">Optimal</div>
</div> </div>
<span>100%</span> <span>100%</span>

View File

@ -1,21 +1,95 @@
import React from 'react' import React, { useState, useEffect } from 'react'
import { WalletIcon } from '../../../icons/3dChartIcons' import { Line } from 'react-chartjs-2'
import useChartStore from '../../../../store/useChartStore';
import { useWidgetStore } from '../../../../store/useWidgetStore'; import { useWidgetStore } from '../../../../store/useWidgetStore';
import axios from 'axios';
import io from "socket.io-client";
import { WalletIcon } from '../../../icons/3dChartIcons';
const TotalCardComponent = ({ const TotalCardComponent = ({
object object
}: any) => { }: any) => {
const { setSelectedChartId } = const [ progress, setProgress ] = useState<any>(0)
useWidgetStore(); 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 ( return (
<> <>
<div className="header-wrapper" onClick={() => { <div className="header-wrapper" >
setSelectedChartId(object.id) <div className="header">{name}</div>
}}>
<div className="header">{object.header}</div>
<div className="data-values"> <div className="data-values">
<div className="value">{object.value}</div> <div className="value">{progress}</div>
<div className="per">{object.per}</div> <div className="per">{object.per}</div>
</div> </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 { Line } from 'react-chartjs-2'
import useChartStore from '../../../../store/useChartStore';
import { useWidgetStore } from '../../../../store/useWidgetStore';
import axios from 'axios'; import axios from 'axios';
import io from "socket.io-client";
const WarehouseThroughputComponent = ({ const WarehouseThroughputComponent = ({
object object
@ -8,6 +11,17 @@ const WarehouseThroughputComponent = ({
const [measurements, setmeasurements] = useState<any>({}); const [measurements, setmeasurements] = useState<any>({});
const [duration, setDuration] = useState("1h") 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 = { const lineGraphData = {
labels: [ 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 ( return (
<> <>
<div className="header"> <div className="header">
<h2>Warehouse Throughput</h2> <h2>{name}</h2>
<p> {/* <p>
<span>(+5) more</span> in 2025 <span>(+5) more</span> in 2025
</p> </p> */}
</div> </div>
<div className="lineGraph" style={{ height: "100%" }}> <div className="lineGraph" style={{ height: "100%" }}>
<Line data={lineGraphData} options={lineGraphOptions} /> <Line data={ Object.keys(measurements).length > 0 ? chartData : lineGraphData} options={lineGraphOptions} />
</div> </div>
</> </>
) )

View File

@ -5,22 +5,83 @@ import * as THREE from "three";
import * as Types from "../../../types/world/worldTypes"; import * as Types from "../../../types/world/worldTypes";
import PathNavigator from "./pathNavigator"; import PathNavigator from "./pathNavigator";
import NavMeshDetails from "./navMeshDetails"; import NavMeshDetails from "./navMeshDetails";
import {
useSelectedActionSphere,
useSimulationPaths,
} from "../../../store/store";
const Agv = ({ lines, plane }: { lines: Types.RefLines; plane: Types.RefMesh; }) => { const Agv = ({
const pathPoints = useMemo(() => [ lines,
[ plane,
{ x: 8.477161935339709, y: 0, z: 17.41343083550102 }, }: {
{ x: 9.175416491482693, y: 0, z: -12.361001232663693 }, lines: Types.RefLines;
], plane: Types.RefMesh;
// [ }) => {
// { x: 13.508213355232144, y: 0, z: -15.456970649652018 }, const [pathPoints, setPathPoints] = useState<
// { x: -30.464866520869617, y: 0, z: 9.779806557688929 }, {
// ], uuid: string;
[ points: { x: number; y: number; z: number }[];
{ x: 16.792040856420844, y: 0, z: 15.86281907549489 }, }[]
{ x: -42.77173264503395, y: 0, z: -15.821322764400804 }, >([]);
], const { simulationPaths } = useSimulationPaths();
], []); const { selectedActionSphere } = useSelectedActionSphere();
useEffect(() => {
if (!Array.isArray(simulationPaths)) {
} else {
let agvModels = simulationPaths.filter(
(val: any) => val.modelName === "agv"
);
let findMesh = agvModels.filter(
(val: any) =>
val.modeluuid === selectedActionSphere?.path?.modeluuid &&
val.type === "Vehicle"
);
const result =
findMesh.length > 0 &&
findMesh[0].type === "Vehicle" &&
typeof findMesh[0].point?.actions.start === "object" &&
typeof findMesh[0].point?.actions.end === "object" &&
"x" in findMesh[0].point.actions.start &&
"y" in findMesh[0].point.actions.start &&
"x" in findMesh[0].point.actions.end &&
"y" in findMesh[0].point.actions.end
? [
{
uuid: findMesh[0].modeluuid, // Ensure it's a number
points: [
{
x: findMesh[0].position[0],
y: findMesh[0].position[1],
z: findMesh[0].position[2],
},
{
x: findMesh[0].point.actions.start.x,
y: 0,
z: findMesh[0].point.actions.start.y,
},
{
x: findMesh[0].point.actions.end.x,
y: 0,
z: findMesh[0].point.actions.end.y,
},
],
},
]
: [];
if (result.length > 0) {
setPathPoints((prev) => {
const existingUUIDs = new Set(prev.map((item) => item.uuid));
const newItems = result.filter(
(item) => !existingUUIDs.has(item.uuid)
);
return [...prev, ...newItems];
});
}
}
}, [simulationPaths, selectedActionSphere]);
let groupRef = useRef() as Types.RefGroup; let groupRef = useRef() as Types.RefGroup;
const [navMesh, setNavMesh] = useState(); const [navMesh, setNavMesh] = useState();
@ -35,7 +96,12 @@ const Agv = ({ lines, plane }: { lines: Types.RefLines; plane: Types.RefMesh; })
plane={plane} plane={plane}
/> />
{pathPoints.map((pair, i) => ( {pathPoints.map((pair, i) => (
<PathNavigator navMesh={navMesh} selectedPoints={pair} key={i} /> <PathNavigator
navMesh={navMesh}
selectedPoints={pair.points}
id={pair.uuid}
key={i}
/>
))} ))}
<group ref={groupRef} visible={false} name="Meshes"></group> <group ref={groupRef} visible={false} name="Meshes"></group>
</> </>

View File

@ -35,10 +35,14 @@ export default function NavMeshDetails({
const [positions, indices] = getPositionsAndIndices(meshes); const [positions, indices] = getPositionsAndIndices(meshes);
const cs = 0.25; const cs = 0.25;
const ch = 0.5; const ch = 0.69;
const walkableRadius = 0.5; const walkableRadius = 0.5;
const { success, navMesh } = generateSoloNavMesh(positions, indices, { cs, ch, walkableRadius: Math.round(walkableRadius / ch), }); const { success, navMesh } = generateSoloNavMesh(positions, indices, {
cs,
ch,
walkableRadius: Math.round(walkableRadius / ch),
});
if (!success || !navMesh) { if (!success || !navMesh) {
return; return;
@ -49,7 +53,7 @@ export default function NavMeshDetails({
const debugDrawer = new DebugDrawer(); const debugDrawer = new DebugDrawer();
debugDrawer.drawNavMesh(navMesh); debugDrawer.drawNavMesh(navMesh);
// scene.add(debugDrawer); // scene.add(debugDrawer);
} catch (error) { } } catch (error) {}
}; };
initializeNavigation(); initializeNavigation();

View File

@ -1,98 +1,149 @@
import React, { useEffect, useState, useRef } from "react"; import React, { useEffect, useState, useRef } from "react";
import * as THREE from "three"; import * as THREE from "three";
import { useFrame } from "@react-three/fiber"; import { useFrame, useThree } from "@react-three/fiber";
import { NavMeshQuery } from "@recast-navigation/core"; import { NavMeshQuery } from "@recast-navigation/core";
import { Line } from "@react-three/drei"; import { Line } from "@react-three/drei";
import { useTh } from "leva/dist/declarations/src/styles";
import { useActiveTool } from "../../../store/store";
// Define interface for props // Define interface for props
interface PathNavigatorProps { interface PathNavigatorProps {
navMesh: any; navMesh: any;
selectedPoints: any; selectedPoints: any;
id: string;
} }
export default function PathNavigator({ export default function PathNavigator({
navMesh, navMesh,
selectedPoints, selectedPoints,
id,
}: PathNavigatorProps) { }: PathNavigatorProps) {
const [path, setPath] = useState<[number, number, number][]>([]); const [path, setPath] = useState<[number, number, number][]>([]);
const progressRef = useRef(0); const progressRef = useRef(0);
const distancesRef = useRef<number[]>([]);
const totalDistanceRef = useRef(0);
const currentSegmentIndex = useRef(0);
const { scene } = useThree();
const { activeTool } = useActiveTool();
const [startPoint, setStartPoint] = useState(new THREE.Vector3());
const meshRef = useRef<THREE.Mesh | null>(null); const meshRef = useRef<THREE.Mesh | null>(null);
useEffect(() => { useEffect(() => {
if (selectedPoints.length === 2 && navMesh) { if (!scene || !id || path.length < 2) return;
const [start, end] = selectedPoints;
if (!start || !end) return;
const navMeshQuery = new NavMeshQuery(navMesh); let totalDistance = 0;
const distances: number[] = [];
const { path: computedPath } = navMeshQuery.computePath(start, end); for (let i = 0; i < path.length - 1; i++) {
const start = new THREE.Vector3(...path[i]);
if (computedPath.length > 0) { const end = new THREE.Vector3(...path[i + 1]);
setPath(computedPath.map(({ x, y, z }) => [x, y + 0.1, z])); const segmentDistance = start.distanceTo(end);
progressRef.current = 0; distances.push(segmentDistance);
} totalDistance += segmentDistance;
} }
}, [selectedPoints, navMesh]);
distancesRef.current = distances;
totalDistanceRef.current = totalDistance;
progressRef.current = 0; // Reset progress when the path changes
}, [path]);
useEffect(() => {
if (!navMesh || selectedPoints.length === 0) return;
// Flatten the selectedPoints array into a single list of points
const allPoints = selectedPoints.flat();
// Compute paths between consecutive points
const computedPath: [number, number, number][] = [];
for (let i = 0; i < allPoints.length - 1; i++) {
const start = allPoints[i];
setStartPoint(
new THREE.Vector3(allPoints[0].x, allPoints[0].y, allPoints[0].z)
);
const end = allPoints[i + 1];
try {
const navMeshQuery = new NavMeshQuery(navMesh);
const { path: segmentPath } = navMeshQuery.computePath(start, end);
if (segmentPath && segmentPath.length > 0) {
computedPath.push(
...segmentPath.map(({ x, y, z }): [number, number, number] => [
x,
y + 0.1,
z,
])
);
}
} catch (error) {}
}
// Set the full computed path
if (computedPath.length > 0) {
setPath(computedPath);
currentSegmentIndex.current = 0; // Reset to the first segment
}
}, [selectedPoints, navMesh, path]);
useFrame((_, delta) => { useFrame((_, delta) => {
if (path.length > 1 && meshRef.current) { if (!scene || !id || path.length < 2) return;
const speed = 3;
progressRef.current += delta * speed;
let totalDistance = 0; // Find the object in the scene
const distances: number[] = []; const findObject = scene.getObjectByProperty("uuid", id);
for (let i = 0; i < path.length - 1; i++) { if (activeTool === "play") {
const start = new THREE.Vector3(...path[i]); if (!findObject) return;
const end = new THREE.Vector3(...path[i + 1]);
const segmentDistance = start.distanceTo(end); const speed = 5;
distances.push(segmentDistance); progressRef.current += delta * speed;
totalDistance += segmentDistance;
}
let coveredDistance = progressRef.current; let coveredDistance = progressRef.current;
let accumulatedDistance = 0; let accumulatedDistance = 0;
let index = 0; let index = 0;
// Determine the current segment of the path
while ( while (
index < distances.length && index < distancesRef.current.length &&
coveredDistance > accumulatedDistance + distances[index] coveredDistance > accumulatedDistance + distancesRef.current[index]
) { ) {
accumulatedDistance += distances[index]; accumulatedDistance += distancesRef.current[index];
index++; index++;
} }
if (index < distances.length) { // If the object has reached the end of the path, stop moving
const start = new THREE.Vector3(...path[index]); if (index >= distancesRef.current.length) {
const end = new THREE.Vector3(...path[index + 1]); progressRef.current = totalDistanceRef.current;
const segmentDistance = distances[index]; return;
const t = (coveredDistance - accumulatedDistance) / segmentDistance;
const position = start.clone().lerp(end, t); // Use clone() to avoid mutating the original vector
meshRef.current.position.copy(position);
const direction = new THREE.Vector3()
.subVectors(end, start)
.normalize();
const targetQuaternion = new THREE.Quaternion().setFromUnitVectors(
new THREE.Vector3(0, 0, 1),
direction
);
meshRef.current.quaternion.slerp(targetQuaternion, 0.1);
} else {
progressRef.current = totalDistance;
} }
// Interpolate position within the current segment
const start = new THREE.Vector3(...path[index]);
const end = new THREE.Vector3(...path[index + 1]);
const segmentDistance = distancesRef.current[index];
const t = (coveredDistance - accumulatedDistance) / segmentDistance;
const position = start.clone().lerp(end, t);
findObject.position.copy(position);
// Rotate the object to face the direction of movement
const direction = new THREE.Vector3().subVectors(end, start).normalize();
const targetQuaternion = new THREE.Quaternion().setFromUnitVectors(
new THREE.Vector3(0, 0, 1), // Assuming forward direction is (0, 0, 1)
direction
);
findObject.quaternion.slerp(targetQuaternion, 0.1); // Smoothly interpolate rotation
} else if (activeTool === "cursor") {
findObject?.position.copy(startPoint);
} }
}); });
return ( return (
<> <>
{/* {path.length > 0 && <Line points={path} color="blue" lineWidth={3} />} */} {path.length > 0 && <Line points={path} color="blue" lineWidth={3} />}
{path.length > 0 && ( {/* {path.length > 0 && (
<mesh ref={meshRef} position={path.length > 0 ? path[0] : [0, 0.1, 0]}> <mesh ref={meshRef} position={path.length > 0 ? path[0] : [0, 0.1, 0]}>
<boxGeometry args={[1, 1, 1]} /> <boxGeometry args={[1, 1, 1]} />
<meshNormalMaterial /> <meshNormalMaterial />
</mesh> </mesh>
)} )} */}
</> </>
); );
} }

View File

@ -1,10 +1,26 @@
import { useFrame, useThree } from "@react-three/fiber"; 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 assetVisibility from "../geomentries/assets/assetVisibility";
import { useEffect } from "react"; import { useEffect } from "react";
import * as THREE from "three"; import * as THREE from "three";
import * as Types from "../../../types/world/worldTypes"; 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 { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader"; import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader";
import DeletableHoveredFloorItems from "../geomentries/assets/deletableHoveredFloorItems"; import DeletableHoveredFloorItems from "../geomentries/assets/deletableHoveredFloorItems";
@ -14,318 +30,405 @@ import addAssetModel from "../geomentries/assets/addAssetModel";
import { getFloorAssets } from "../../../services/factoryBuilder/assest/floorAsset/getFloorItemsApi"; import { getFloorAssets } from "../../../services/factoryBuilder/assest/floorAsset/getFloorItemsApi";
import useModuleStore from "../../../store/useModuleStore"; import useModuleStore from "../../../store/useModuleStore";
// import { retrieveGLTF } from "../../../utils/indexDB/idbUtils"; // import { retrieveGLTF } from "../../../utils/indexDB/idbUtils";
const assetManagerWorker = new Worker(new URL('../../../services/factoryBuilder/webWorkers/assetManagerWorker.js', import.meta.url)); const assetManagerWorker = new Worker(
const gltfLoaderWorker = new Worker(new URL('../../../services/factoryBuilder/webWorkers/gltfLoaderWorker.js', import.meta.url)); 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 FloorItemsGroup = ({
const state: Types.ThreeState = useThree(); itemsGroup,
const { raycaster, controls }: any = state; hoveredDeletableFloorItem,
const { renderDistance } = useRenderDistance(); AttachedObject,
const { toggleView } = useToggleView(); floorGroup,
const { floorItems, setFloorItems } = useFloorItems(); tempLoader,
const { camMode } = useCamMode(); isTempLoader,
const { deleteModels } = useDeleteModels(); plane,
const { setDeletableFloorItem } = useDeletableFloorItem(); }: any) => {
const { transformMode } = useTransformMode(); const state: Types.ThreeState = useThree();
const { setselectedFloorItem } = useselectedFloorItem(); const { raycaster, controls }: any = state;
const { activeTool } = useActiveTool(); const { renderDistance } = useRenderDistance();
const { selectedItem, setSelectedItem } = useSelectedItem(); const { toggleView } = useToggleView();
const { setLoadingProgress } = useLoadingProgress(); const { floorItems, setFloorItems } = useFloorItems();
const { activeModule } = useModuleStore(); const { camMode } = useCamMode();
const { socket } = useSocketStore(); const { deleteModels } = useDeleteModels();
const loader = new GLTFLoader(); const { setDeletableFloorItem } = useDeletableFloorItem();
const dracoLoader = new DRACOLoader(); 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/'); dracoLoader.setDecoderPath(
loader.setDRACOLoader(dracoLoader); "https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/libs/draco/gltf/"
);
loader.setDRACOLoader(dracoLoader);
useEffect(() => { useEffect(() => {
const email = localStorage.getItem('email'); const email = localStorage.getItem("email");
const organization = (email!.split("@")[1]).split(".")[0]; const organization = email!.split("@")[1].split(".")[0];
let totalAssets = 0; let totalAssets = 0;
let loadedAssets = 0; let loadedAssets = 0;
const updateLoadingProgress = (progress: number) => { const updateLoadingProgress = (progress: number) => {
if (progress < 100) { if (progress < 100) {
setLoadingProgress(progress); setLoadingProgress(progress);
} else if (progress === 100) { } else if (progress === 100) {
setTimeout(() => { setTimeout(() => {
setLoadingProgress(100); setLoadingProgress(100);
setTimeout(() => { setTimeout(() => {
setLoadingProgress(0); setLoadingProgress(0);
}, 1500); }, 1500);
}, 1000); }, 1000);
} }
}; };
getFloorAssets(organization).then((data) => { getFloorAssets(organization).then((data) => {
const uniqueItems = (data as Types.FloorItems).filter((item, index, self) => const uniqueItems = (data as Types.FloorItems).filter(
index === self.findIndex((t) => t.modelfileID === item.modelfileID) (item, index, self) =>
); index === self.findIndex((t) => t.modelfileID === item.modelfileID)
totalAssets = uniqueItems.length; );
if (totalAssets === 0) { totalAssets = uniqueItems.length;
updateLoadingProgress(100); if (totalAssets === 0) {
return; updateLoadingProgress(100);
} return;
gltfLoaderWorker.postMessage({ floorItems: data }); }
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) => { useEffect(() => {
if (event.data.message === "gltfLoaded" && event.data.modelBlob) { assetManagerWorker.onmessage = async (event) => {
const blobUrl = URL.createObjectURL(event.data.modelBlob); cancelOngoingTasks(); // Cancel the ongoing process
await assetManager(event.data, itemsGroup, loader);
};
}, [assetManagerWorker]);
loader.load(blobUrl, (gltf) => { useEffect(() => {
URL.revokeObjectURL(blobUrl); if (toggleView) return;
THREE.Cache.remove(blobUrl);
THREE.Cache.add(event.data.modelID, gltf);
loadedAssets++; const uuids: string[] = [];
const progress = Math.round((loadedAssets / totalAssets) * 100); itemsGroup.current?.children.forEach((child: any) => {
updateLoadingProgress(progress); uuids.push(child.uuid);
});
const cameraPosition = state.camera.position;
if (loadedAssets === totalAssets) { assetManagerWorker.postMessage({
loadInitialFloorItems(itemsGroup, setFloorItems); floorItems,
updateLoadingProgress(100); cameraPosition,
} uuids,
}); renderDistance,
} });
}; }, [camMode, renderDistance]);
}, []);
useEffect(() => { useEffect(() => {
assetManagerWorker.onmessage = async (event) => { const controls: any = state.controls;
cancelOngoingTasks(); // Cancel the ongoing process const camera: any = state.camera;
await assetManager(event.data, itemsGroup, loader);
};
}, [assetManagerWorker]);
useEffect(() => { if (controls) {
if (toggleView) return let intervalId: NodeJS.Timeout | null = null;
const handleChange = () => {
if (toggleView) return;
const uuids: string[] = []; const uuids: string[] = [];
itemsGroup.current?.children.forEach((child: any) => { 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 }); assetManagerWorker.postMessage({
}, [camMode, renderDistance]); floorItems,
cameraPosition,
uuids,
renderDistance,
});
};
useEffect(() => { const startInterval = () => {
const controls: any = state.controls; if (!intervalId) {
const camera: any = state.camera; intervalId = setInterval(handleChange, 50);
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);
}
};
} }
}, [state.controls, floorItems, toggleView, renderDistance]); };
useEffect(() => { const stopInterval = () => {
const canvasElement = state.gl.domElement; handleChange();
let drag = false; if (intervalId) {
let isLeftMouseDown = false; clearInterval(intervalId);
intervalId = null;
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 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; return () => {
controls.removeEventListener("rest", handleChange);
if (selectedItem.id !== "" && event.dataTransfer?.files[0]) { controls.removeEventListener("rest", stopInterval);
addAssetModel(raycaster, state.camera, state.pointer, floorGroup, setFloorItems, itemsGroup, isTempLoader, tempLoader, socket, selectedItem, setSelectedItem, plane); controls.removeEventListener("control", startInterval);
} controls.removeEventListener("controlend", stopInterval);
if (intervalId) {
clearInterval(intervalId);
} }
};
}
}, [state.controls, floorItems, toggleView, renderDistance]);
const onDragOver = (event: any) => { useEffect(() => {
event.preventDefault(); const canvasElement = state.gl.domElement;
}; let drag = false;
let isLeftMouseDown = false;
if (activeModule === "builder") { const onMouseDown = (evt: any) => {
canvasElement.addEventListener("mousedown", onMouseDown); if (evt.button === 0) {
canvasElement.addEventListener("mouseup", onMouseUp); isLeftMouseDown = true;
canvasElement.addEventListener("mousemove", onMouseMove); drag = false;
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 () => { const onMouseMove = () => {
canvasElement.removeEventListener("mousedown", onMouseDown); if (isLeftMouseDown) {
canvasElement.removeEventListener("mouseup", onMouseUp); drag = true;
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(() => { const onMouseUp = async (evt: any) => {
// console.log('floorItems: ', floorItems); if (controls) {
}, [floorItems]) (controls as any).enabled = true;
}
if (evt.button === 0) {
isLeftMouseDown = false;
if (drag) return;
useFrame(() => {
if (controls)
assetVisibility(itemsGroup, state.camera.position, renderDistance);
if (deleteModels) { if (deleteModels) {
DeletableHoveredFloorItems(state, itemsGroup, hoveredDeletableFloorItem, setDeletableFloorItem); DeleteFloorItems(
} else if (!deleteModels) { itemsGroup,
if (hoveredDeletableFloorItem.current) { hoveredDeletableFloorItem,
hoveredDeletableFloorItem.current = undefined; setFloorItems,
setDeletableFloorItem(null); socket
} );
} }
}) const Mode = transformMode;
return ( if (Mode !== null || activeTool === "cursor") {
<group ref={itemsGroup} name="itemsGroup"> if (!itemsGroup.current) return;
</group> 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

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

View File

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

View File

@ -1,22 +1,52 @@
import { useToggleView } from '../../../store/store'; import { useTileDistance, useToggleView } from "../../../store/store";
import * as CONSTANTS from '../../../types/world/worldConstants'; import * as CONSTANTS from "../../../types/world/worldConstants";
const Ground = ({ grid, plane }: any) => { const Ground = ({ grid, plane }: any) => {
const { toggleView } = useToggleView(); const { toggleView } = useToggleView();
const savedTheme: string | null = localStorage.getItem('theme'); const savedTheme: string | null = localStorage.getItem("theme");
const { planeValue, gridValue } = useTileDistance();
return ( return (
<mesh name="Ground">
<mesh name="Ground"> <mesh
<mesh ref={grid} name="Grid" position={!toggleView ? CONSTANTS.gridConfig.position3D : CONSTANTS.gridConfig.position2D}> ref={grid}
<gridHelper args={[CONSTANTS.gridConfig.size, CONSTANTS.gridConfig.divisions, CONSTANTS.gridConfig.primaryColor, CONSTANTS.gridConfig.secondaryColor]} /> name="Grid"
</mesh> position={
<mesh ref={plane} rotation-x={CONSTANTS.planeConfig.rotation} position={!toggleView ? CONSTANTS.planeConfig.position3D : CONSTANTS.planeConfig.position2D} name="Plane" receiveShadow> !toggleView
<planeGeometry args={[CONSTANTS.planeConfig.width, CONSTANTS.planeConfig.height]} /> ? CONSTANTS.gridConfig.position3D
<meshBasicMaterial color={CONSTANTS.planeConfig.color} /> : CONSTANTS.gridConfig.position2D
</mesh> }
</mesh> >
) <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; export default Ground;

View File

@ -1,9 +1,22 @@
import { useRef, useEffect} from 'react'; import { useRef, useEffect } from "react";
import { useThree } from '@react-three/fiber'; import { useThree } from "@react-three/fiber";
import * as THREE from 'three'; import * as THREE from "three";
import { useAzimuth, useElevation, useShadows, useSunPosition, useFloorItems, useWallItems } from '../../../store/store'; import {
import * as CONSTANTS from '../../../types/world/worldConstants'; useAzimuth,
const shadowWorker = new Worker(new URL('../../../services/factoryBuilder/webWorkers/shadowWorker', import.meta.url)); 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() { export default function Shadows() {
const { shadows, setShadows } = useShadows(); const { shadows, setShadows } = useShadows();
@ -15,6 +28,7 @@ export default function Shadows() {
const { azimuth, setAzimuth } = useAzimuth(); const { azimuth, setAzimuth } = useAzimuth();
const { floorItems } = useFloorItems(); const { floorItems } = useFloorItems();
const { wallItems } = useWallItems(); const { wallItems } = useWallItems();
const { planeValue } = useTileDistance();
useEffect(() => { useEffect(() => {
gl.shadowMap.enabled = true; gl.shadowMap.enabled = true;
@ -48,9 +62,9 @@ export default function Shadows() {
useEffect(() => { useEffect(() => {
if (controls && shadows) { if (controls && shadows) {
updateShadows(); updateShadows();
(controls as any).addEventListener('update', updateShadows); (controls as any).addEventListener("update", updateShadows);
return () => { return () => {
(controls as any).removeEventListener('update', updateShadows); (controls as any).removeEventListener("update", updateShadows);
}; };
} }
}, [controls, elevation, azimuth, shadows]); }, [controls, elevation, azimuth, shadows]);
@ -75,10 +89,24 @@ export default function Shadows() {
shadow-normalBias={CONSTANTS.shadowConfig.shadownormalBias} shadow-normalBias={CONSTANTS.shadowConfig.shadownormalBias}
/> />
<object3D ref={targetRef} /> <object3D ref={targetRef} />
<mesh position={CONSTANTS.shadowConfig.shadowMaterialPosition} rotation={CONSTANTS.shadowConfig.shadowMaterialRotation} receiveShadow> <mesh
<planeGeometry args={[CONSTANTS.planeConfig.width, CONSTANTS.planeConfig.height]} /> position={CONSTANTS.shadowConfig.shadowMaterialPosition}
<shadowMaterial opacity={CONSTANTS.shadowConfig.shadowMaterialOpacity} transparent /> 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> </mesh>
</> </>
); );
} }

View File

@ -30,6 +30,8 @@ import {
useWalls, useWalls,
useToolMode, useToolMode,
useRefTextUpdate, useRefTextUpdate,
useRenderDistance,
useLimitDistance,
} from "../../../store/store"; } from "../../../store/store";
////////// 3D Function Imports ////////// ////////// 3D Function Imports //////////
@ -52,6 +54,7 @@ import Layer2DVisibility from "../../builder/geomentries/layers/layer2DVisibilit
import DrieHtmlTemp from "../mqttTemp/drieHtmlTemp"; import DrieHtmlTemp from "../mqttTemp/drieHtmlTemp";
import ZoneGroup from "../../builder/groups/zoneGroup"; import ZoneGroup from "../../builder/groups/zoneGroup";
import Agv from "../../builder/agv/agv"; import Agv from "../../builder/agv/agv";
import useModuleStore from "../../../store/useModuleStore";
export default function World() { export default function World() {
const state = useThree<Types.ThreeState>(); // Importing the state from the useThree hook, which contains the scene, camera, and other Three.js elements. const state = useThree<Types.ThreeState>(); // Importing the state from the useThree hook, which contains the scene, camera, and other Three.js elements.
@ -117,9 +120,12 @@ export default function World() {
const { roofVisibility, setRoofVisibility } = useRoofVisibility(); const { roofVisibility, setRoofVisibility } = useRoofVisibility();
const { wallVisibility, setWallVisibility } = useWallVisibility(); const { wallVisibility, setWallVisibility } = useWallVisibility();
const { shadows, setShadows } = useShadows(); const { shadows, setShadows } = useShadows();
const { renderDistance, setRenderDistance } = useRenderDistance();
const { limitDistance, setLimitDistance } = useLimitDistance();
const { updateScene, setUpdateScene } = useUpdateScene(); const { updateScene, setUpdateScene } = useUpdateScene();
const { walls, setWalls } = useWalls(); const { walls, setWalls } = useWalls();
const { refTextupdate, setRefTextUpdate } = useRefTextUpdate(); const { refTextupdate, setRefTextUpdate } = useRefTextUpdate();
const { activeModule } = useModuleStore();
// const loader = new GLTFLoader(); // const loader = new GLTFLoader();
// const dracoLoader = new DRACOLoader(); // const dracoLoader = new DRACOLoader();
@ -200,6 +206,8 @@ export default function World() {
setRoofVisibility(visibility.roofVisibility); setRoofVisibility(visibility.roofVisibility);
setWallVisibility(visibility.wallVisibility); setWallVisibility(visibility.wallVisibility);
setShadows(visibility.shadowVisibility); setShadows(visibility.shadowVisibility);
setRenderDistance(visibility.renderDistance);
setLimitDistance(visibility.limitDistance);
} }
} }
fetchVisibility(); fetchVisibility();
@ -359,8 +367,7 @@ export default function World() {
/> />
{/* <DrieHtmlTemp itemsGroup={itemsGroup} /> */} {/* <DrieHtmlTemp itemsGroup={itemsGroup} /> */}
{activeModule === "simulation" && <Agv lines={lines} plane={plane} />}
<Agv lines={lines} plane={plane} />
</> </>
); );
} }

View File

@ -1,299 +1,362 @@
import * as THREE from 'three'; import * as THREE from "three";
import * as Types from '../../../types/world/worldTypes'; import * as Types from "../../../types/world/worldTypes";
import { useRef, useState, useEffect, useMemo } from 'react'; import { useRef, useState, useEffect, useMemo } from "react";
import { Sphere, TransformControls } from '@react-three/drei'; import { Sphere, TransformControls } from "@react-three/drei";
import { useEditingPoint, useEyeDropMode, useIsConnecting, usePreviewPosition, useRenderDistance, useSelectedActionSphere, useSelectedPath, useSimulationPaths } from '../../../store/store'; import {
import { useFrame, useThree } from '@react-three/fiber'; useEditingPoint,
import { useSubModuleStore } from '../../../store/useModuleStore'; useEyeDropMode,
useIsConnecting,
usePreviewPosition,
useRenderDistance,
useSelectedActionSphere,
useSelectedPath,
useSimulationPaths,
} from "../../../store/store";
import { useFrame, useThree } from "@react-three/fiber";
import { useSubModuleStore } from "../../../store/useModuleStore";
function PathCreation({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObject<THREE.Group> }) { function PathCreation({
const { renderDistance } = useRenderDistance(); pathsGroupRef,
const { setSubModule } = useSubModuleStore(); }: {
const { setSelectedActionSphere, selectedActionSphere } = useSelectedActionSphere(); pathsGroupRef: React.MutableRefObject<THREE.Group>;
const { eyeDropMode, setEyeDropMode } = useEyeDropMode(); }) {
const { editingPoint, setEditingPoint } = useEditingPoint(); const { renderDistance } = useRenderDistance();
const { previewPosition, setPreviewPosition } = usePreviewPosition(); const { setSubModule } = useSubModuleStore();
const { raycaster, camera, pointer, gl } = useThree(); const { setSelectedActionSphere, selectedActionSphere } =
const plane = useMemo(() => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0), []); useSelectedActionSphere();
const { setSelectedPath } = useSelectedPath(); const { eyeDropMode, setEyeDropMode } = useEyeDropMode();
const { simulationPaths, setSimulationPaths } = useSimulationPaths(); const { editingPoint, setEditingPoint } = useEditingPoint();
const { isConnecting } = useIsConnecting(); const { previewPosition, setPreviewPosition } = usePreviewPosition();
const { raycaster, camera, pointer, gl } = useThree();
const groupRefs = useRef<{ [key: string]: THREE.Group }>({}); const plane = useMemo(
const sphereRefs = useRef<{ [key: string]: THREE.Mesh }>({}); () => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0),
const isMovingRef = useRef(false); []
const transformRef = useRef<any>(null); );
const [transformMode, setTransformMode] = useState<'translate' | 'rotate' | null>(null); const { setSelectedPath } = useSelectedPath();
const { simulationPaths, setSimulationPaths } = useSimulationPaths();
const { isConnecting } = useIsConnecting();
useEffect(() => { const groupRefs = useRef<{ [key: string]: THREE.Group }>({});
setTransformMode(null); const sphereRefs = useRef<{ [key: string]: THREE.Mesh }>({});
const handleKeyDown = (e: KeyboardEvent) => { const isMovingRef = useRef(false);
if (!selectedActionSphere) return; const transformRef = useRef<any>(null);
if (e.key === 'g') { const [transformMode, setTransformMode] = useState<
setTransformMode(prev => prev === 'translate' ? null : 'translate'); "translate" | "rotate" | null
} >(null);
if (e.key === 'r') {
setTransformMode(prev => prev === 'rotate' ? null : 'rotate');
}
};
window.addEventListener('keydown', handleKeyDown); useEffect(() => {
return () => window.removeEventListener('keydown', handleKeyDown); setTransformMode(null);
}, [selectedActionSphere]); const handleKeyDown = (e: KeyboardEvent) => {
if (!selectedActionSphere) return;
useFrame(() => { if (e.key === "g") {
Object.values(groupRefs.current).forEach(group => { setTransformMode((prev) => (prev === "translate" ? null : "translate"));
if (group) { }
const distance = new THREE.Vector3(...group.position.toArray()).distanceTo(camera.position); if (e.key === "r") {
group.visible = distance <= renderDistance; setTransformMode((prev) => (prev === "rotate" ? null : "rotate"));
} }
});
});
const updateSimulationPaths = () => {
if (!selectedActionSphere) return;
const updatedPaths = simulationPaths.map((path) => {
if (path.type === "Conveyor") {
return {
...path,
points: path.points.map((point) =>
point.uuid === selectedActionSphere.point.uuid
? {
...point,
position: [
selectedActionSphere.point.position.x,
selectedActionSphere.point.position.y,
selectedActionSphere.point.position.z,
],
rotation: [
selectedActionSphere.point.rotation.x,
selectedActionSphere.point.rotation.y,
selectedActionSphere.point.rotation.z,
]
}
: point
),
};
}
return path;
}) as Types.ConveyorEventsSchema[];
setSimulationPaths(updatedPaths);
}; };
useFrame(() => { window.addEventListener("keydown", handleKeyDown);
if (eyeDropMode) { return () => window.removeEventListener("keydown", handleKeyDown);
raycaster.setFromCamera(pointer, camera); }, [selectedActionSphere]);
const intersectionPoint = new THREE.Vector3();
const point = raycaster.ray.intersectPlane(plane, intersectionPoint);
if (point) { useFrame(() => {
setPreviewPosition({ x: point.x, y: point.z }); Object.values(groupRefs.current).forEach((group) => {
} if (group) {
} else { const distance = new THREE.Vector3(
setPreviewPosition(null); ...group.position.toArray()
).distanceTo(camera.position);
group.visible = distance <= renderDistance;
}
});
});
const updateSimulationPaths = () => {
if (!selectedActionSphere) return;
const updatedPaths = simulationPaths.map((path) => {
if (path.type === "Conveyor") {
return {
...path,
points: path.points.map((point) =>
point.uuid === selectedActionSphere.point.uuid
? {
...point,
position: [
selectedActionSphere.point.position.x,
selectedActionSphere.point.position.y,
selectedActionSphere.point.position.z,
],
rotation: [
selectedActionSphere.point.rotation.x,
selectedActionSphere.point.rotation.y,
selectedActionSphere.point.rotation.z,
],
}
: point
),
};
}
return path;
}) as Types.ConveyorEventsSchema[];
setSimulationPaths(updatedPaths);
};
useFrame(() => {
if (eyeDropMode) {
raycaster.setFromCamera(pointer, camera);
const intersectionPoint = new THREE.Vector3();
const point = raycaster.ray.intersectPlane(plane, intersectionPoint);
if (point) {
setPreviewPosition({ x: point.x, y: point.z });
}
} else {
setPreviewPosition(null);
}
});
useEffect(() => {
if (!camera) return;
const canvasElement = gl.domElement;
canvasElement.tabIndex = 0;
const onPointerDown = () => {
isMovingRef.current = false;
};
const onPointerMove = () => {
isMovingRef.current = true;
};
const onPointerUp = (event: PointerEvent) => {
if (
!isMovingRef.current &&
eyeDropMode &&
event.button === 0 &&
previewPosition
) {
event.preventDefault();
if (editingPoint) {
handlePointUpdate(editingPoint, previewPosition.x, previewPosition.y);
setEditingPoint(null);
setEyeDropMode(false);
} }
}
};
if (eyeDropMode) {
canvasElement.addEventListener("pointerdown", onPointerDown);
canvasElement.addEventListener("pointermove", onPointerMove);
canvasElement.addEventListener("pointerup", onPointerUp);
}
return () => {
canvasElement.removeEventListener("pointerdown", onPointerDown);
canvasElement.removeEventListener("pointermove", onPointerMove);
canvasElement.removeEventListener("pointerup", onPointerUp);
};
}, [eyeDropMode, editingPoint, previewPosition]);
const handlePointUpdate = (
pointType: "start" | "end",
x: number,
z: number
) => {
if (!selectedActionSphere?.point?.uuid) return;
const updatedPaths = simulationPaths.map((path) => {
if (
path.type === "Vehicle" &&
path.point.uuid === selectedActionSphere.point.uuid
) {
return {
...path,
point: {
...path.point,
actions: {
...path.point.actions,
[pointType]: {
...path.point.actions[pointType],
x: x,
y: z,
},
},
},
};
}
return path;
}); });
useEffect(() => { setSimulationPaths(updatedPaths);
if (!camera) return; };
const canvasElement = gl.domElement;
canvasElement.tabIndex = 0;
return (
<group name="simulation-simulationPaths-group" ref={pathsGroupRef}>
{simulationPaths.map((path) => {
if (path.type === "Conveyor") {
const points = path.points.map(
(point) => new THREE.Vector3(...point.position)
);
const onPointerDown = () => { return (
isMovingRef.current = false; <group
}; name={`${path.modeluuid}-event-path`}
key={path.modeluuid}
const onPointerMove = () => { ref={(el) => (groupRefs.current[path.modeluuid] = el!)}
isMovingRef.current = true; position={path.position}
}; rotation={path.rotation}
onClick={(e) => {
const onPointerUp = (event: PointerEvent) => { if (isConnecting || eyeDropMode) return;
if (!isMovingRef.current && eyeDropMode && event.button === 0 && previewPosition) { e.stopPropagation();
event.preventDefault(); setSelectedPath({
if (editingPoint) { path,
handlePointUpdate(editingPoint, previewPosition.x, previewPosition.y); group: groupRefs.current[path.modeluuid],
setEditingPoint(null); });
setEyeDropMode(false); setSelectedActionSphere(null);
} setTransformMode(null);
} setSubModule("mechanics");
}; }}
onPointerMissed={() => {
if (eyeDropMode) { if (eyeDropMode) return;
canvasElement.addEventListener("pointerdown", onPointerDown); setSelectedPath(null);
canvasElement.addEventListener("pointermove", onPointerMove); setSubModule("properties");
canvasElement.addEventListener("pointerup", onPointerUp); }}
} >
{path.points.map((point, index) => (
return () => { <Sphere
canvasElement.removeEventListener("pointerdown", onPointerDown); key={point.uuid}
canvasElement.removeEventListener("pointermove", onPointerMove); uuid={point.uuid}
canvasElement.removeEventListener("pointerup", onPointerUp); position={point.position}
}; args={[0.15, 32, 32]}
}, [eyeDropMode, editingPoint, previewPosition]); name="events-sphere"
ref={(el) => (sphereRefs.current[point.uuid] = el!)}
const handlePointUpdate = (pointType: 'start' | 'end', x: number, z: number) => { onClick={(e) => {
if (!selectedActionSphere?.point?.uuid) return; if (isConnecting || eyeDropMode) return;
e.stopPropagation();
const updatedPaths = simulationPaths.map((path) => { setSelectedActionSphere({
if (path.type === "Vehicle" && path.point.uuid === selectedActionSphere.point.uuid) { path,
return { point: sphereRefs.current[point.uuid],
...path, });
point: { setSubModule("mechanics");
...path.point, setSelectedPath(null);
actions: { }}
...path.point.actions, userData={{ point, path }}
[pointType]: { onPointerMissed={() => {
...path.point.actions[pointType], if (eyeDropMode) return;
x: x, setSubModule("properties");
y: z setSelectedActionSphere(null);
} }}
} >
<meshStandardMaterial
color={
index === 0
? "orange"
: index === path.points.length - 1
? "blue"
: "green"
} }
}; />
} </Sphere>
return path; ))}
});
setSimulationPaths(updatedPaths); {points.slice(0, -1).map((point, index) => {
}; const nextPoint = points[index + 1];
const segmentCurve = new THREE.CatmullRomCurve3([
point,
nextPoint,
]);
const tubeGeometry = new THREE.TubeGeometry(
segmentCurve,
20,
0.1,
16,
false
);
return ( return (
<group name='simulation-simulationPaths-group' ref={pathsGroupRef}> <mesh
{simulationPaths.map((path) => { name="event-connection-tube"
if (path.type === 'Conveyor') { key={`tube-${index}`}
const points = path.points.map(point => new THREE.Vector3(...point.position)); geometry={tubeGeometry}
>
<meshStandardMaterial
transparent
opacity={0.9}
color="red"
/>
</mesh>
);
})}
</group>
);
} else if (path.type === "Vehicle") {
return (
<group
name={`${path.modeluuid}-vehicle-path`}
key={path.modeluuid}
ref={(el) => (groupRefs.current[path.modeluuid] = el!)}
position={path.position}
onClick={(e) => {
if (isConnecting || eyeDropMode) return;
e.stopPropagation();
setSelectedPath({
path,
group: groupRefs.current[path.modeluuid],
});
setSelectedActionSphere(null);
setTransformMode(null);
setSubModule("mechanics");
}}
onPointerMissed={() => {
if (eyeDropMode) return;
setSelectedPath(null);
setSubModule("properties");
}}
>
<Sphere
key={path.point.uuid}
uuid={path.point.uuid}
position={path.point.position}
args={[0.15, 32, 32]}
name="events-sphere"
ref={(el) => (sphereRefs.current[path.point.uuid] = el!)}
onClick={(e) => {
if (isConnecting || eyeDropMode) return;
e.stopPropagation();
setSelectedActionSphere({
path,
point: sphereRefs.current[path.point.uuid],
});
setSubModule("mechanics");
setSelectedPath(null);
}}
userData={{ point: path.point, path }}
onPointerMissed={() => {
if (eyeDropMode) return;
setSubModule("properties");
setSelectedActionSphere(null);
}}
>
<meshStandardMaterial color="purple" />
</Sphere>
</group>
);
}
return null;
})}
return ( {selectedActionSphere && transformMode && (
<group <TransformControls
name={`${path.modeluuid}-event-path`} ref={transformRef}
key={path.modeluuid} object={selectedActionSphere.point}
ref={el => (groupRefs.current[path.modeluuid] = el!)} mode={transformMode}
position={path.position} onMouseUp={updateSimulationPaths}
rotation={path.rotation} />
onClick={(e) => { )}
if (isConnecting || eyeDropMode) return; </group>
e.stopPropagation(); );
setSelectedPath({ path, group: groupRefs.current[path.modeluuid] });
setSelectedActionSphere(null);
setTransformMode(null);
setSubModule('mechanics');
}}
onPointerMissed={() => {
if (eyeDropMode) return;
setSelectedPath(null);
setSubModule('properties');
}}
>
{path.points.map((point, index) => (
<Sphere
key={point.uuid}
uuid={point.uuid}
position={point.position}
args={[0.15, 32, 32]}
name='events-sphere'
ref={el => (sphereRefs.current[point.uuid] = el!)}
onClick={(e) => {
if (isConnecting || eyeDropMode) return;
e.stopPropagation();
setSelectedActionSphere({
path,
point: sphereRefs.current[point.uuid]
});
setSubModule('mechanics');
setSelectedPath(null);
}}
userData={{ point, path }}
onPointerMissed={() => {
if (eyeDropMode) return;
setSubModule('properties');
setSelectedActionSphere(null);
}}
>
<meshStandardMaterial
color={index === 0 ? 'orange' : index === path.points.length - 1 ? 'blue' : 'green'}
/>
</Sphere>
))}
{points.slice(0, -1).map((point, index) => {
const nextPoint = points[index + 1];
const segmentCurve = new THREE.CatmullRomCurve3([point, nextPoint]);
const tubeGeometry = new THREE.TubeGeometry(segmentCurve, 20, 0.1, 16, false);
return (
<mesh name='event-connection-tube' key={`tube-${index}`} geometry={tubeGeometry}>
<meshStandardMaterial transparent opacity={0.9} color="red" />
</mesh>
);
})}
</group>
);
} else if (path.type === 'Vehicle') {
return (
<group
name={`${path.modeluuid}-vehicle-path`}
key={path.modeluuid}
ref={el => (groupRefs.current[path.modeluuid] = el!)}
position={path.position}
onClick={(e) => {
if (isConnecting || eyeDropMode) return;
e.stopPropagation();
setSelectedPath({ path, group: groupRefs.current[path.modeluuid] });
setSelectedActionSphere(null);
setTransformMode(null);
setSubModule('mechanics');
}}
onPointerMissed={() => {
if (eyeDropMode) return;
setSelectedPath(null);
setSubModule('properties');
}}
>
<Sphere
key={path.point.uuid}
uuid={path.point.uuid}
position={path.point.position}
args={[0.15, 32, 32]}
name='events-sphere'
ref={el => (sphereRefs.current[path.point.uuid] = el!)}
onClick={(e) => {
if (isConnecting || eyeDropMode) return;
e.stopPropagation();
setSelectedActionSphere({
path,
point: sphereRefs.current[path.point.uuid]
});
setSubModule('mechanics');
setSelectedPath(null);
}}
userData={{ point: path.point, path }}
onPointerMissed={() => {
if (eyeDropMode) return;
setSubModule('properties');
setSelectedActionSphere(null);
}}
>
<meshStandardMaterial color="purple" />
</Sphere>
</group>
);
}
return null;
})}
{selectedActionSphere && transformMode && (
<TransformControls
ref={transformRef}
object={selectedActionSphere.point}
mode={transformMode}
onMouseUp={updateSimulationPaths}
/>
)}
</group>
);
} }
export default PathCreation; export default PathCreation;

View File

@ -10,6 +10,7 @@ import PathCreation from "./path/pathCreation";
import PathConnector from "./path/pathConnector"; import PathConnector from "./path/pathConnector";
import useModuleStore from "../../store/useModuleStore"; import useModuleStore from "../../store/useModuleStore";
import ProcessContainer from "./process/processContainer"; import ProcessContainer from "./process/processContainer";
import Agv from "../builder/agv/agv";
function Simulation() { function Simulation() {
const { activeModule } = useModuleStore(); const { activeModule } = useModuleStore();
@ -41,6 +42,7 @@ function Simulation() {
<PathCreation pathsGroupRef={pathsGroupRef} /> <PathCreation pathsGroupRef={pathsGroupRef} />
<PathConnector pathsGroupRef={pathsGroupRef} /> <PathConnector pathsGroupRef={pathsGroupRef} />
<ProcessContainer /> <ProcessContainer />
{/* <Agv /> */}
</> </>
)} )}
</> </>

View File

@ -1,4 +1,5 @@
import { saveTemplateApi } from "../../services/realTimeVisulization/zoneData/saveTempleteApi"; import { saveTemplateApi } from "../../services/realTimeVisulization/zoneData/saveTempleteApi";
import { useSocketStore } from "../../store/store";
import { Template } from "../../store/useTemplateStore"; import { Template } from "../../store/useTemplateStore";
import { captureVisualization } from "./captureVisualization"; import { captureVisualization } from "./captureVisualization";
@ -11,6 +12,7 @@ type HandleSaveTemplateProps = {
widgets: any[]; widgets: any[];
}; };
templates?: Template[]; templates?: Template[];
visualizationSocket: any;
}; };
// Generate a unique ID // Generate a unique ID
@ -24,6 +26,7 @@ export const handleSaveTemplate = async ({
widgets3D, widgets3D,
selectedZone, selectedZone,
templates = [], templates = [],
visualizationSocket,
}: HandleSaveTemplateProps): Promise<void> => { }: HandleSaveTemplateProps): Promise<void> => {
try { try {
// Check if the selected zone has any widgets // Check if the selected zone has any widgets
@ -47,7 +50,7 @@ export const handleSaveTemplate = async ({
// Capture visualization snapshot // Capture visualization snapshot
const snapshot = await captureVisualization(); const snapshot = await captureVisualization();
console.log("snapshot: ", snapshot);
// if (!snapshot) { // if (!snapshot) {
// return; // return;
// } // }
@ -72,18 +75,25 @@ export const handleSaveTemplate = async ({
console.error("Organization could not be determined from email."); console.error("Organization could not be determined from email.");
return; return;
} }
let saveTemplate = {
organization: organization,
template: newTemplate,
};
if (visualizationSocket) {
visualizationSocket.emit("v2:viz-template:add", saveTemplate);
}
// Save the template // Save the template
try { try {
const response = await saveTemplateApi(organization, newTemplate); addTemplate(newTemplate);
console.log("Save API Response:", response); // const response = await saveTemplateApi(organization, newTemplate);
// console.log("Save API Response:", response);
// Add template only if API call succeeds // Add template only if API call succeeds
addTemplate(newTemplate);
} catch (apiError) { } catch (apiError) {
console.error("Error saving template to API:", apiError); // console.error("Error saving template to API:", apiError);
} }
} catch (error) { } catch (error) {
console.error("Error in handleSaveTemplate:", error); // console.error("Error in handleSaveTemplate:", error);
} }
}; };

View File

@ -0,0 +1,194 @@
import { useEffect } from "react";
import { useSocketStore } from "../../store/store";
import { useSelectedZoneStore } from "../../store/useZoneStore";
import { useDroppedObjectsStore } from "../../store/useDroppedObjectsStore";
import { useZoneWidgetStore } from "../../store/useZone3DWidgetStore";
import useTemplateStore from "../../store/useTemplateStore";
export default function SocketRealTimeViz() {
const { visualizationSocket } = useSocketStore();
const { selectedZone, setSelectedZone } = useSelectedZoneStore();
const deleteObject = useDroppedObjectsStore((state) => state.deleteObject);
const duplicateObject = useDroppedObjectsStore((state) => state.duplicateObject);
const updateObjectPosition = useDroppedObjectsStore((state) => state.updateObjectPosition);
const { addWidget } = useZoneWidgetStore()
const { templates, removeTemplate } = useTemplateStore();
const { setTemplates } = useTemplateStore();
useEffect(() => {
const email = localStorage.getItem("email") || "";
const organization = email?.split("@")[1]?.split(".")[0];
if (visualizationSocket) {
//add panel response
visualizationSocket.on("viz-panel:response:updates", (addPanel: any) => {
console.log('addPanel: ', addPanel);
if (addPanel.success) {
let addPanelData = addPanel.data.data
setSelectedZone(addPanelData)
}
})
//delete panel response
visualizationSocket.on("viz-panel:response:delete", (deletePanel: any) => {
console.log('deletePanel: ', deletePanel);
if (deletePanel.success) {
let deletePanelData = deletePanel.data.data
setSelectedZone(deletePanelData)
}
})
// add 2dWidget response
visualizationSocket.on("viz-widget:response:updates", (add2dWidget: any) => {
console.log('add2dWidget: ', add2dWidget);
if (add2dWidget.success && add2dWidget.data) {
setSelectedZone((prev) => {
const isWidgetAlreadyAdded = prev.widgets.some(
(widget) => widget.id === add2dWidget.data.widgetData.id
);
if (isWidgetAlreadyAdded) return prev; // Prevent duplicate addition
return {
...prev,
zoneId: add2dWidget.data.zoneId,
zoneName: add2dWidget.data.zoneName,
widgets: [...prev.widgets, add2dWidget.data.widgetData], // Append new widget
};
});
}
});
//delete 2D Widget response
visualizationSocket.on("viz-widget:response:delete", (deleteWidget: any) => {
console.log('deleteWidget: ', deleteWidget);
if (deleteWidget?.success && deleteWidget.data) {
setSelectedZone((prevZone: any) => ({
...prevZone,
zoneId: deleteWidget.data.zoneId,
zoneName: deleteWidget.data.zoneName,
widgets: deleteWidget.data.widgetDeleteDatas, // Replace with new widget list
}));
}
});
//add Floating Widget response
visualizationSocket.on("viz-float:response:updates", (addFloatingWidget: any) => {
console.log('addFloatingWidget: ', addFloatingWidget);
if (addFloatingWidget.success) {
if (addFloatingWidget.success && addFloatingWidget.message === "FloatWidget created successfully") {
const state = useDroppedObjectsStore.getState();
const zone = state.zones[addFloatingWidget.data.zoneName];
if (!zone) {
state.setZone(addFloatingWidget.data.zoneName, addFloatingWidget.data.zoneId);
}
const existingObjects = zone ? zone.objects : [];
const newWidget = addFloatingWidget.data.widget;
// ✅ Check if the widget ID already exists before adding
const isAlreadyAdded = existingObjects.some(obj => obj.id === newWidget.id);
if (isAlreadyAdded) {
return; // Don't add the widget if it already exists
}
// Add widget only if it doesn't exist
state.addObject(addFloatingWidget.data.zoneName, newWidget);
}
if (addFloatingWidget.message === "Widget updated successfully") {
updateObjectPosition(addFloatingWidget.data.zoneName, addFloatingWidget.data.index, addFloatingWidget.data.position);
}
}
});
//duplicate Floating Widget response
visualizationSocket.on("viz-float:response:addDuplicate", (duplicateFloatingWidget: any) => {
console.log('duplicateFloatingWidget: ', duplicateFloatingWidget);
if (duplicateFloatingWidget.success && duplicateFloatingWidget.message === "duplicate FloatWidget created successfully") {
useDroppedObjectsStore.setState((state) => {
const zone = state.zones[duplicateFloatingWidget.data.zoneName];
if (!zone) return state; // Zone doesn't exist, return state as is
const existingObjects = zone.objects;
const newWidget = duplicateFloatingWidget.data.widget;
// ✅ Check if the object with the same ID already exists
const isAlreadyAdded = existingObjects.some(obj => obj.id === newWidget.id);
if (isAlreadyAdded) {
return state; // Don't update state if it's already there
}
return {
zones: {
...state.zones,
[duplicateFloatingWidget.data.zoneName]: {
...zone,
objects: [...existingObjects, newWidget], // Append only if it's not a duplicate
},
},
};
});
}
});
//delete Floating Widget response
visualizationSocket.on("viz-float:response:delete", (deleteFloatingWidget: any) => {
console.log('deleteFloatingWidget: ', deleteFloatingWidget);
if (deleteFloatingWidget.success) {
deleteObject(deleteFloatingWidget.data.zoneName, deleteFloatingWidget.data.floatWidgetID);
}
});
//add 3D Widget response
visualizationSocket.on("viz-widget3D:response:updates", (add3DWidget: any) => {
console.log('add3DWidget: ', add3DWidget);
if (add3DWidget.success) {
if (add3DWidget.message === "Widget created successfully") {
addWidget(add3DWidget.data.zoneId, add3DWidget.data.widget);
}
}
});
// add Template response
visualizationSocket.on("viz-template:response:add", (addingTemplate: any) => {
console.log('addingTemplate: ', addingTemplate);
if (addingTemplate.success) {
if (addingTemplate.message === "Template saved successfully") {
setTemplates(addingTemplate.data);
}
}
});
//load Template response
visualizationSocket.on("viz-template:response:addTemplateZone", (loadTemplate: any) => {
console.log('loadTemplate: ', loadTemplate);
if (loadTemplate.success) {
if (loadTemplate.message === "Template placed in Zone") {
let template = loadTemplate.data.template
setSelectedZone({
panelOrder: template.panelOrder,
activeSides: Array.from(new Set(template.panelOrder)), // No merging with previous `activeSides`
widgets: template.widgets,
});
useDroppedObjectsStore.getState().setZone(template.zoneName, template.zoneId);
if (Array.isArray(template.floatingWidget)) {
template.floatingWidget.forEach((val: any) => {
useDroppedObjectsStore.getState().addObject(template.zoneName, val);
});
}
}
}
});
//delete Template response
visualizationSocket.on("viz-template:response:delete", (deleteTemplate: any) => {
console.log('deleteTemplate: ', deleteTemplate);
if (deleteTemplate.success) {
if (deleteTemplate.message === 'Template deleted successfully') {
removeTemplate(deleteTemplate.data);
}
}
});
}
}, [visualizationSocket])
return (
<></>
)
}

View File

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

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}`; let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`;
export const findEnvironment = async (organization: string, userId: string) => { export const findEnvironment = async (organization: string, userId: string) => {
try { try {
const response = await fetch(`${url_Backend_dwinzo}/api/v1/findEnvironments/${organization}/${userId}`, { const response = await fetch(
method: "GET", `${url_Backend_dwinzo}/api/v1/findEnvironments/${organization}/${userId}`,
headers: { {
"Content-Type": "application/json", method: "GET",
}, headers: {
}); "Content-Type": "application/json",
},
}
);
if (!response.ok) { if (!response.ok) {
throw new Error("Failed to get wall and roof visibility"); 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");
}
} }
};
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}`; 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) => { export const setEnvironment = async (
try { organization: string,
const response = await fetch(`${url_Backend_dwinzo}/api/v1/setEvironments`, { userId: string,
method: "POST", wallVisibility: Boolean,
headers: { roofVisibility: Boolean,
"Content-Type": "application/json", shadowVisibility: Boolean,
}, renderDistance: number,
body: JSON.stringify({ organization, userId, wallVisibility, roofVisibility, shadowVisibility }), 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) { if (!response.ok) {
throw new Error("Failed to set wall and roof visibility"); 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");
}
} }
};
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"); throw new Error("Network response was not ok");
} }
const result = await response.json(); const result = await response.json();
const last10Assets = result.slice(-10); // const last10Assets = result.slice(-10);
console.log('last10Assets: ', last10Assets); // console.log('last10Assets: ', last10Assets);
return result; return result;
} catch (error) { } catch (error) {
console.log("error: ", error); console.log("error: ", error);

View File

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

View File

@ -0,0 +1,34 @@
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`;
// let url_Backend_dwinzo = `http://192.168.0.102:5000`;
export const deletePanelApi = async (
zoneId: string,
panelName: string,
organization: string
) => {
console.log('panelName: ', panelName);
console.log('organization: ', organization);
console.log('zoneId: ', zoneId);
try {
const response = await fetch(`${url_Backend_dwinzo}/api/v2/panel/delete`, {
method: "PATCH",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ organization, zoneId, panelName }),
});
if (!response.ok) {
throw new Error("Failed to delete widget in the zone");
}
const result = await response.json();
return result;
} catch (error) {
if (error instanceof Error) {
throw new Error(error.message);
} else {
throw new Error("An unknown error occurred");
}
}
};

View File

@ -3,21 +3,24 @@ let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_UR
export const deleteWidgetApi = async ( export const deleteWidgetApi = async (
widgetID: string, widgetID: string,
organization: string organization: string,
zoneId:string
) => { ) => {
console.log('zoneId: ', zoneId);
console.log('organization: ', organization);
console.log('widgetID: ', widgetID);
try { try {
const response = await fetch(`${url_Backend_dwinzo}/api/v2/delete/widget`, { const response = await fetch(`${url_Backend_dwinzo}/api/v2/delete/widget`, {
method: "PATCH", method: "PATCH",
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
}, },
body: JSON.stringify({ organization, widgetID }), body: JSON.stringify({ organization, widgetID,zoneId }),
}); });
if (!response.ok) { if (!response.ok) {
throw new Error("Failed to delete widget in the zone"); throw new Error("Failed to delete widget in the zone");
} }
const result = await response.json(); const result = await response.json();
return result; return result;
} catch (error) { } catch (error) {

View File

@ -1,8 +0,0 @@
import { create } from "zustand";
const useFloatingDataStore = create((set) => ({
floatingdata: [], // Initial state
setfloatingadata: (newData: []) => set({ floatingdata: newData }), // Setter function
}));
export default useFloatingDataStore;

View File

@ -11,16 +11,28 @@ export const useSocketStore = create<any>((set: any, get: any) => ({
return; return;
} }
const socket = io(`http://${process.env.REACT_APP_SERVER_SOCKET_API_BASE_URL}/Builder`, { const socket = io(
reconnection: false, `http://${process.env.REACT_APP_SERVER_SOCKET_API_BASE_URL}/Builder`,
auth: { email, organization }, {
}); reconnection: false,
auth: { email, organization },
}
);
set({ socket }); const visualizationSocket = io(
`http://${process.env.REACT_APP_SERVER_SOCKET_API_BASE_URL}/Visualization`,
{
reconnection: false,
auth: { email, organization },
}
);
set({ socket, visualizationSocket });
}, },
disconnectSocket: () => { disconnectSocket: () => {
set((state: any) => { set((state: any) => {
state.socket?.disconnect(); state.socket?.disconnect();
state.visualizationSocket?.disconnect();
return { socket: null }; return { socket: null };
}); });
}, },
@ -205,7 +217,9 @@ export const useActiveLayer = create<any>((set: any) => ({
interface RefTextUpdateState { interface RefTextUpdateState {
refTextupdate: number; refTextupdate: number;
setRefTextUpdate: (callback: (currentValue: number) => number | number) => void; setRefTextUpdate: (
callback: (currentValue: number) => number | number
) => void;
} }
export const useRefTextUpdate = create<RefTextUpdateState>((set) => ({ export const useRefTextUpdate = create<RefTextUpdateState>((set) => ({
@ -213,7 +227,9 @@ export const useRefTextUpdate = create<RefTextUpdateState>((set) => ({
setRefTextUpdate: (callback) => setRefTextUpdate: (callback) =>
set((state) => ({ set((state) => ({
refTextupdate: refTextupdate:
typeof callback === "function" ? callback(state.refTextupdate) : callback, typeof callback === "function"
? callback(state.refTextupdate)
: callback,
})), })),
})); }));
@ -248,7 +264,7 @@ export const useAzimuth = create<any>((set: any) => ({
})); }));
export const useRenderDistance = create<any>((set: any) => ({ export const useRenderDistance = create<any>((set: any) => ({
renderDistance: 50, renderDistance: 40,
setRenderDistance: (x: any) => set({ renderDistance: x }), setRenderDistance: (x: any) => set({ renderDistance: x }),
})); }));
@ -393,4 +409,22 @@ export const useWidgetSubOption = create<any>((set: any) => ({
widgetSubOption: "2D", widgetSubOption: "2D",
setWidgetSubOption: (x: any) => set({ widgetSubOption: x }), 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; interval: number;
duration: string; duration: string;
name: string; name: string;
header: string;
flotingDuration: string;
flotingMeasurements: Record<string, Measurement>; // Change array to Record<string, Measurement>
setMeasurements: (newMeasurements: Record<string, Measurement>) => void; setMeasurements: (newMeasurements: Record<string, Measurement>) => void;
updateDuration: (newDuration: string) => void; updateDuration: (newDuration: string) => void;
updateName: (newName: 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) => ({ const useChartStore = create<MeasurementStore>((set) => ({
@ -20,6 +26,9 @@ const useChartStore = create<MeasurementStore>((set) => ({
interval: 1000, interval: 1000,
duration: "1h", duration: "1h",
name:'', name:'',
header:'',
flotingDuration: "1h",
flotingMeasurements: {},
setMeasurements: (newMeasurements) => setMeasurements: (newMeasurements) =>
set(() => ({ measurements: newMeasurements })), set(() => ({ measurements: newMeasurements })),
@ -28,7 +37,16 @@ const useChartStore = create<MeasurementStore>((set) => ({
set(() => ({ duration: newDuration })), set(() => ({ duration: newDuration })),
updateName: (newName) => 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; export default useChartStore;

View File

@ -1,5 +1,6 @@
import { create } from "zustand"; import { create } from "zustand";
import { addingFloatingWidgets } from "../services/realTimeVisulization/zoneData/addFloatingWidgets"; import { addingFloatingWidgets } from "../services/realTimeVisulization/zoneData/addFloatingWidgets";
import { useSocketStore } from "./store";
type DroppedObject = { type DroppedObject = {
className: string; className: string;
@ -36,7 +37,7 @@ type DroppedObjectsState = {
bottom: number | "auto"; bottom: number | "auto";
} }
) => void; ) => void;
deleteObject: (zoneName: string, index: number) => void; // Add this line deleteObject: (zoneName: string, id: string) => void; // Add this line
duplicateObject: (zoneName: string, index: number) => void; // Add this line duplicateObject: (zoneName: string, index: number) => void; // Add this line
}; };
@ -77,15 +78,16 @@ export const useDroppedObjectsStore = create<DroppedObjectsState>((set) => ({
}; };
}), }),
deleteObject: (zoneName: string, index: number) => deleteObject: (zoneName: string, id: string) =>
set((state) => { set((state) => {
const zone = state.zones[zoneName]; const zone = state.zones[zoneName];
if (!zone) return state; if (!zone) return state;
return { return {
zones: { zones: {
[zoneName]: { [zoneName]: {
...zone, ...zone,
objects: zone.objects.filter((_, i) => i !== index), // Remove object at the given index objects: zone.objects.filter((obj) => obj.id !== id), // Remove object at the given index
}, },
}, },
}; };
@ -93,6 +95,8 @@ export const useDroppedObjectsStore = create<DroppedObjectsState>((set) => ({
duplicateObject: async (zoneName: string, index: number) => { duplicateObject: async (zoneName: string, index: number) => {
const state = useDroppedObjectsStore.getState(); // Get the current state const state = useDroppedObjectsStore.getState(); // Get the current state
const zone = state.zones[zoneName]; const zone = state.zones[zoneName];
let socketState = useSocketStore.getState();
let visualizationSocket = socketState.visualizationSocket;
if (!zone) return; if (!zone) return;
@ -119,8 +123,28 @@ export const useDroppedObjectsStore = create<DroppedObjectsState>((set) => ({
}, },
}; };
console.log("zone: ", zone.zoneId); let duplicateFloatingWidget = {
console.log("duplicatedObject: ", duplicatedObject); organization: organization,
widget: duplicatedObject,
zoneId: zone.zoneId,
index: index,
};
if (visualizationSocket) {
visualizationSocket.emit(
"v2:viz-float:addDuplicate",
duplicateFloatingWidget
);
}
useDroppedObjectsStore.setState((state) => ({
zones: {
...state.zones,
[zoneName]: {
...state.zones[zoneName],
objects: [...state.zones[zoneName].objects, duplicatedObject], // Append duplicated object
},
},
}));
// Make async API call outside of Zustand set function // Make async API call outside of Zustand set function
// let response = await addingFloatingWidgets( // let response = await addingFloatingWidgets(
@ -131,15 +155,7 @@ export const useDroppedObjectsStore = create<DroppedObjectsState>((set) => ({
// if (response.message === "FloatWidget created successfully") { // if (response.message === "FloatWidget created successfully") {
// Update the state inside `set` // Update the state inside `set`
useDroppedObjectsStore.setState((state) => ({
zones: {
...state.zones,
[zoneName]: {
...state.zones[zoneName],
objects: [...state.zones[zoneName].objects, duplicatedObject], // Append duplicated object
},
},
}));
// } // }
}, },
})); }));

View File

@ -0,0 +1,33 @@
import { create } from "zustand";
type WidgetData = {
id: string;
type: string;
position: [number, number, number];
};
type ZoneWidgetStore = {
zoneWidgetData: Record<string, WidgetData[]>;
setZoneWidgetData: (zoneId: string, widgets: WidgetData[]) => void;
addWidget: (zoneId: string, widget: WidgetData) => void;
};
export const useZoneWidgetStore = create<ZoneWidgetStore>((set) => ({
zoneWidgetData: {},
setZoneWidgetData: (zoneId, widgets) =>
set((state) => ({
zoneWidgetData: {
...state.zoneWidgetData,
[zoneId]: widgets,
},
})),
addWidget: (zoneId, widget) =>
set((state) => ({
zoneWidgetData: {
...state.zoneWidgetData,
[zoneId]: [...(state.zoneWidgetData[zoneId] || []), widget],
},
})),
}));

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 { .input-value {
width: 40px; width: 42px;
text-align: center; text-align: center;
&::-webkit-inner-spin-button, &::-webkit-inner-spin-button,
&::-webkit-outer-spin-button { &::-webkit-outer-spin-button {

View File

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

View File

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

View File

@ -26,7 +26,7 @@
border-radius: $border-radius-medium; border-radius: $border-radius-medium;
padding: 18px; padding: 18px;
position: absolute; position: absolute;
z-index: 1000; z-index: 1;
} }
.scene-container { .scene-container {
@ -172,6 +172,7 @@
border-radius: 6px; border-radius: 6px;
overflow: visible !important; overflow: visible !important;
z-index: $z-index-tools; z-index: $z-index-tools;
overflow: auto;
.panel-content { .panel-content {
position: relative; position: relative;
@ -320,6 +321,10 @@
bottom: 0; bottom: 0;
} }
} }
.panel.hidePanel {
opacity: 0;
}
} }
.playingFlase { .playingFlase {
@ -545,20 +550,21 @@
.floating-wrapper { .floating-wrapper {
.icon { .icon {
width: 25px !important; // width: 25px !important;
height: 25px !important; // height: 25px !important;
background-color: transparent; // background-color: transparent;
} }
.kebab { .kebab {
width: 30px; width: 25px;
height: 30px; height: 25px;
position: absolute !important; position: absolute !important;
top: 0px; top: 0px;
right: 0px; right: 0px;
z-index: 10; z-index: 10;
cursor: pointer; cursor: pointer;
@include flex-center; @include flex-center;
background-color: transparent !important;
} }
.kebab-options { .kebab-options {
@ -576,6 +582,12 @@
box-shadow: var(--box-shadow-medium); box-shadow: var(--box-shadow-medium);
.icon {
width: 25px !important;
height: 25px !important;
background-color: transparent;
}
.btn { .btn {
display: flex; display: flex;
gap: 6px; gap: 6px;
@ -607,18 +619,19 @@
} }
} }
.dublicate {
cursor: not-allowed;
}
} }
} }
.distance-line { .distance-line {
position: absolute; position: absolute;
border-style: dashed; border-style: dashed;
border-color: var(--accent-color); /* Green color for visibility */ border-color: var(--accent-color);
/* Green color for visibility */
border-width: 1px; 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; z-index: 10000;
} }
@ -631,51 +644,112 @@
padding: 2px 6px; padding: 2px 6px;
border-radius: 3px; border-radius: 3px;
white-space: nowrap; white-space: nowrap;
transform: translate(-50%, -50%); /* Center the label */ transform: translate(-50%, -50%);
/* Center the label */
} }
/* Specific styles for each type of line */ /* Specific styles for each type of line */
/* Top distance line */ /* Top distance line */
.distance-line.top { .distance-line.top {
border-bottom: none; /* Remove bottom border for a single line */ border-bottom: none;
width: 2px; /* Thin vertical line */ /* Remove bottom border for a single line */
width: 2px;
/* Thin vertical line */
} }
.distance-line.top .distance-label { .distance-line.top .distance-label {
top: -10px; /* Position label above the line */ top: -10px;
left: 50%; /* Center horizontally */ /* Position label above the line */
left: 50%;
/* Center horizontally */
} }
/* Bottom distance line */ /* Bottom distance line */
.distance-line.bottom { .distance-line.bottom {
border-top: none; /* Remove top border for a single line */ border-top: none;
width: 2px; /* Thin vertical line */ /* Remove top border for a single line */
width: 2px;
/* Thin vertical line */
} }
.distance-line.bottom .distance-label { .distance-line.bottom .distance-label {
bottom: -10px; /* Position label below the line */ bottom: -10px;
left: 50%; /* Center horizontally */ /* Position label below the line */
left: 50%;
/* Center horizontally */
} }
/* Left distance line */ /* Left distance line */
.distance-line.left { .distance-line.left {
border-right: none; /* Remove right border for a single line */ border-right: none;
height: 2px; /* Thin horizontal line */ /* Remove right border for a single line */
height: 2px;
/* Thin horizontal line */
} }
.distance-line.left .distance-label { .distance-line.left .distance-label {
left: -10px; /* Position label to the left of the line */ left: -10px;
top: 50%; /* Center vertically */ /* Position label to the left of the line */
top: 50%;
/* Center vertically */
} }
/* Right distance line */ /* Right distance line */
.distance-line.right { .distance-line.right {
border-left: none; /* Remove left border for a single line */ border-left: none;
height: 2px; /* Thin horizontal line */ /* Remove left border for a single line */
height: 2px;
/* Thin horizontal line */
} }
.distance-line.right .distance-label { .distance-line.right .distance-label {
right: -10px; /* Position label to the right of the line */ right: -10px;
top: 50%; /* Center vertically */ /* 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 = { export type GridConfig = {
size: number; size: number;
divisions: number; divisions: number;
primaryColor: string; primaryColor: string;
secondaryColor: string; secondaryColor: string;
position2D: [x: number, y: number, z: number]; position2D: [x: number, y: number, z: number];
position3D: [x: number, y: number, z: number]; position3D: [x: number, y: number, z: number];
} };
export type PlaneConfig = { export type PlaneConfig = {
position2D: [x: number, y: number, z: number]; position2D: [x: number, y: number, z: number];
position3D: [x: number, y: number, z: number]; position3D: [x: number, y: number, z: number];
rotation: number; rotation: number;
width: number; width: number;
height: number; height: number;
color: string; color: string;
} };
export type ShadowConfig = { export type ShadowConfig = {
shadowOffset: number, shadowOffset: number;
shadowmapSizewidth: number, shadowmapSizewidth: number;
shadowmapSizeheight: number, shadowmapSizeheight: number;
shadowcamerafar: number, shadowcamerafar: number;
shadowcameranear: number, shadowcameranear: number;
shadowcameratop: number, shadowcameratop: number;
shadowcamerabottom: number, shadowcamerabottom: number;
shadowcameraleft: number, shadowcameraleft: number;
shadowcameraright: number, shadowcameraright: number;
shadowbias: number, shadowbias: number;
shadownormalBias: number, shadownormalBias: number;
shadowMaterialPosition: [x: number, y: number, z: number], shadowMaterialPosition: [x: number, y: number, z: number];
shadowMaterialRotation: [x: number, y: number, z: number], shadowMaterialRotation: [x: number, y: number, z: number];
shadowMaterialOpacity: number, shadowMaterialOpacity: number;
} };
export type SkyConfig = { export type SkyConfig = {
defaultTurbidity: number; defaultTurbidity: number;
@ -109,34 +109,34 @@ export type AssetConfig = {
}; };
export type PointConfig = { export type PointConfig = {
defaultInnerColor: string; defaultInnerColor: string;
defaultOuterColor: string; defaultOuterColor: string;
deleteColor: string; deleteColor: string;
boxScale: [number, number, number]; boxScale: [number, number, number];
wallOuterColor: string; wallOuterColor: string;
floorOuterColor: string; floorOuterColor: string;
aisleOuterColor: string; aisleOuterColor: string;
zoneOuterColor: string; zoneOuterColor: string;
snappingThreshold: number; snappingThreshold: number;
} };
export type LineConfig = { export type LineConfig = {
tubularSegments: number; tubularSegments: number;
radius: number; radius: number;
radialSegments: number; radialSegments: number;
wallName: string; wallName: string;
floorName: string; floorName: string;
aisleName: string; aisleName: string;
zoneName: string; zoneName: string;
referenceName: string; referenceName: string;
lineIntersectionPoints: number; lineIntersectionPoints: number;
defaultColor: string; defaultColor: string;
wallColor: string; wallColor: string;
floorColor: string; floorColor: string;
aisleColor: string; aisleColor: string;
zoneColor: string; zoneColor: string;
helperColor: string; helperColor: string;
} };
export type WallConfig = { export type WallConfig = {
defaultColor: string; defaultColor: string;
@ -145,10 +145,10 @@ export type WallConfig = {
}; };
export type FloorConfig = { export type FloorConfig = {
defaultColor: string; defaultColor: string;
height: number; height: number;
textureScale: number; textureScale: number;
} };
export type RoofConfig = { export type RoofConfig = {
defaultColor: string; defaultColor: string;
@ -156,16 +156,16 @@ export type RoofConfig = {
}; };
export type AisleConfig = { export type AisleConfig = {
width: number; width: number;
height: number; height: number;
defaultColor: number; defaultColor: number;
} };
export type ZoneConfig = { export type ZoneConfig = {
defaultColor: string; defaultColor: string;
height: number; height: number;
color: string; color: string;
} };
export type ColumnConfig = { export type ColumnConfig = {
defaultColor: string; defaultColor: string;
@ -242,24 +242,24 @@ export const threeDimension: ThreeDimension = {
export const camPositionUpdateInterval: number = 200; // Interval for updating the camera position export const camPositionUpdateInterval: number = 200; // Interval for updating the camera position
export const gridConfig: GridConfig = { export const gridConfig: GridConfig = {
size: 300, // Size of the grid size: 300, // Size of the grid
divisions: 75, // Number of divisions in the grid divisions: 75, // Number of divisions in the grid
primaryColor: savedTheme === "dark" ? "#131313" : "#d5d5d5", // Primary color of the grid primaryColor: savedTheme === "dark" ? "#131313" : "#d5d5d5", // Primary color of the grid
secondaryColor: savedTheme === "dark" ? "#434343" : "#e3e3e3", // Secondary 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 position2D: [0, 0.1, 0], // Position of the grid in 2D view
position3D: [0, -0.5, 0], // Position of the grid in 3D view position3D: [0, -0.5, 0], // Position of the grid in 3D view
} };
export const planeConfig: PlaneConfig = { export const planeConfig: PlaneConfig = {
position2D: [0, -0.5, 0], // Position of the plane position2D: [0, -0.5, 0], // Position of the plane
position3D: [0, -0.65, 0], // Position of the plane position3D: [0, -0.65, 0], // Position of the plane
rotation: -Math.PI / 2, // Rotation of the plane rotation: -Math.PI / 2, // Rotation of the plane
width: 300, // Width of the plane width: 300, // Width of the plane
height: 300, // Height of the plane height: 300, // Height of the plane
color: savedTheme === "dark" ? "#323232" : "#f3f3f3" // Color of the plane color: savedTheme === "dark" ? "#323232" : "#f3f3f3", // Color of the plane
} };
export const shadowConfig: ShadowConfig = { export const shadowConfig: ShadowConfig = {
shadowOffset: 50, // Offset of the shadow shadowOffset: 50, // Offset of the shadow
@ -349,10 +349,10 @@ export const aisleConfig: AisleConfig = {
}; };
export const zoneConfig: ZoneConfig = { export const zoneConfig: ZoneConfig = {
defaultColor: "black", // Default color of the zones defaultColor: "black", // Default color of the zones
height: 3, height: 3,
color: "#8656DF" // Color of the zones color: "#8656DF", // Color of the zones
} };
export const columnConfig: ColumnConfig = { export const columnConfig: ColumnConfig = {
defaultColor: "White", // Default color of the columns defaultColor: "White", // Default color of the columns

View File

@ -18,4 +18,4 @@ export function toggleTheme() {
localStorage.setItem('theme', newTheme); localStorage.setItem('theme', newTheme);
} }
setTheme(); setTheme();