Merge remote-tracking branch 'origin/main' into simulation-agv

This commit is contained in:
Poovizhi99 2025-04-02 19:12:23 +05:30
commit 69ca2a3121
65 changed files with 4571 additions and 1099 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

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

@ -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,48 @@ 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) { 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,7 +163,11 @@ export const DraggableWidget = ({
id: `${widget.id}-copy-${Date.now()}`, id: `${widget.id}-copy-${Date.now()}`,
}; };
const response = await duplicateWidgetApi(selectedZone.zoneId, organization, duplicatedWidget); const response = await duplicateWidgetApi(
selectedZone.zoneId,
organization,
duplicatedWidget
);
if (response?.message === "Widget created successfully") { if (response?.message === "Widget created successfully") {
setSelectedZone((prevZone: any) => ({ setSelectedZone((prevZone: any) => ({
@ -149,13 +176,11 @@ export const DraggableWidget = ({
})); }));
} }
} catch (error) { } 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 +228,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 +263,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 +283,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 +297,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 +306,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 +315,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,4 +1,3 @@
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, useWidgetSubOption } from "../../../store/store";
@ -16,25 +15,29 @@ import { get3dWidgetZoneData } from "../../../services/realTimeVisulization/zone
import { use3DWidget } from "../../../store/useDroppedObjectsStore"; import { use3DWidget } from "../../../store/useDroppedObjectsStore";
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, setWidgetSubOption } = useWidgetSubOption();
const { selectedZone } = useSelectedZoneStore(); // Get the currently active zone const { selectedZone } = useSelectedZoneStore(); // Get the currently active zone
// 🔥 Store widget data (id, type, position) based on the selected zone // 🔥 Store widget data (id, type, position) based on the selected zone
const [zoneWidgetData, setZoneWidgetData] = useState< const [zoneWidgetData, setZoneWidgetData] = useState<
Record<string, { id: string; type: string; position: [number, number, number] }[]> Record<
>({}); string,
const { setWidgets3D } = use3DWidget() { id: string; type: string; position: [number, number, number] }[]
useEffect(() => { >
if (activeModule !== "visualization") return >({});
if (selectedZone.zoneName === "") return; const { setWidgets3D } = use3DWidget();
useEffect(() => {
if (activeModule !== "visualization") return;
if (selectedZone.zoneName === "") return;
const email = localStorage.getItem("email") || ""; const email = localStorage.getItem("email") || "";
const organization = email?.split("@")[1]?.split(".")[0]; 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);
console.log('result: ', result);
setWidgets3D(result) setWidgets3D(result)
// Ensure the extracted data has id, type, and position correctly mapped // Ensure the extracted data has id, type, and position correctly mapped
const formattedWidgets = result.map((widget: any) => ({ const formattedWidgets = result.map((widget: any) => ({
@ -43,98 +46,108 @@ export default function Dropped3dWidgets() {
position: widget.position, position: widget.position,
})); }));
setZoneWidgetData((prev) => ({ setZoneWidgetData((prev) => ({
...prev, ...prev,
[selectedZone.zoneId]: formattedWidgets, [selectedZone.zoneId]: formattedWidgets,
})); }));
} }
get3dWidgetData(); get3dWidgetData();
}, [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
}, [selectedZone.zoneId,activeModule]); useEffect(() => {
// useEffect(() => { if (activeModule !== "visualization") return;
// // ✅ Set data only for the selected zone, keeping existing state structure if (widgetSubOption === "Floating") return;
// setZoneWidgetData((prev) => ({ if (selectedZone.zoneName === "") return;
// ...prev, const canvasElement = gl.domElement;
// [selectedZone.zoneId]: [ const onDrop = async (event: DragEvent) => {
// { event.preventDefault(); // Prevent default browser behavior
// "id": "1743322674626-50mucpb1c", const email = localStorage.getItem("email") || "";
// "type": "ui-Widget 1", const organization = email?.split("@")[1]?.split(".")[0];
// "position": [120.94655021768133, 4.142360029666558, 124.39283546121099] if (!widgetSelect.startsWith("ui")) return;
// }, const group1 = scene.getObjectByName("itemsGroup");
// { if (!group1) return;
// "id": "1743322682086-je2h9x33v", const intersects = raycaster
// "type": "ui-Widget 2", .intersectObjects(scene.children, true)
// "position": [131.28751045879255, 0.009999999999970264, 133.92059801984362] .filter(
// } (intersect) =>
// ] !intersect.object.name.includes("Roof") &&
// })); !intersect.object.name.includes("agv-collider") &&
// }, [selectedZone.zoneId]); // ✅ Only update when the zone changes !intersect.object.name.includes("MeasurementReference") &&
!intersect.object.userData.isPathObject &&
!(intersect.object.type === "GridHelper")
);
if (intersects.length > 0) {
const { x, y, z } = intersects[0].point;
useEffect(() => { // ✅ Explicitly define position as a tuple
if (activeModule !== "visualization") return; const newWidget: {
if (widgetSubOption === "Floating") return; id: string;
if (selectedZone.zoneName === "") return type: string;
const canvasElement = gl.domElement; position: [number, number, number];
const onDrop = async (event: DragEvent) => { } = {
event.preventDefault(); // Prevent default browser behavior id: generateUniqueId(),
const email = localStorage.getItem("email") || ""; type: widgetSelect,
const organization = email?.split("@")[1]?.split(".")[0]; position: [x, y, z], // Ensures TypeScript recognizes it as a tuple
if (!widgetSelect.startsWith("ui")) return;
const group1 = scene.getObjectByName("itemsGroup");
if (!group1) return;
const intersects = raycaster.intersectObjects(scene.children, true).filter(
(intersect) =>
!intersect.object.name.includes("Roof") &&
!intersect.object.name.includes("agv-collider") &&
!intersect.object.name.includes("MeasurementReference") &&
!intersect.object.userData.isPathObject &&
!(intersect.object.type === "GridHelper")
);
if (intersects.length > 0) {
const { x, y, z } = intersects[0].point;
// ✅ Explicitly define position as a tuple
const newWidget: { id: string; type: string; position: [number, number, number] } = {
id: generateUniqueId(),
type: widgetSelect,
position: [x, y, z], // Ensures TypeScript recognizes it as a tuple
};
let response = await adding3dWidgets(selectedZone.zoneId, organization, newWidget)
// ✅ Store widgets uniquely for each zone
setZoneWidgetData((prev) => ({
...prev,
[selectedZone.zoneId]: [...(prev[selectedZone.zoneId] || []), newWidget],
}));
}
}; };
let response = await adding3dWidgets(
selectedZone.zoneId,
organization,
newWidget
);
canvasElement.addEventListener("drop", onDrop); // ✅ Store widgets uniquely for each zone
return () => { setZoneWidgetData((prev) => ({
canvasElement.removeEventListener("drop", onDrop); ...prev,
}; [selectedZone.zoneId]: [
}, [widgetSelect, activeModule, selectedZone.zoneId, widgetSubOption]); ...(prev[selectedZone.zoneId] || []),
newWidget,
],
}));
}
};
// Get widgets for the currently active zone canvasElement.addEventListener("drop", onDrop);
const activeZoneWidgets = zoneWidgetData[selectedZone.zoneId] || []; return () => {
canvasElement.removeEventListener("drop", onDrop);
};
}, [widgetSelect, activeModule, selectedZone.zoneId, widgetSubOption]);
// Get widgets for the currently active zone
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 +155,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,9 @@ 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 { 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 +45,24 @@ interface DraggingState {
}; };
} }
const DroppedObjects: React.FC = () => { const DroppedObjects: React.FC = () => {
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 +75,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,13 +91,28 @@ 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
} }
@ -93,19 +122,20 @@ const DroppedObjects: React.FC = () => {
const organization = email?.split("@")[1]?.split(".")[0]; const organization = email?.split("@")[1]?.split(".")[0];
let res = await deleteFloatingWidgetApi(id, organization); let res = await deleteFloatingWidgetApi(id, organization);
console.log('res: ', res);
if (res.message === "FloatingWidget deleted successfully") { if (res.message === "FloatingWidget deleted successfully") {
deleteObject(zoneName, index); // Call the deleteObject method from the store 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 +175,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 +367,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 +390,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,6 +418,9 @@ 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
@ -255,22 +430,35 @@ const DroppedObjects: React.FC = () => {
...zone.objects[draggingIndex.index], ...zone.objects[draggingIndex.index],
position: boundedPosition, position: boundedPosition,
}); });
console.log('response: ', response);
if (response.message === "Widget updated successfully") { if (response.message === "Widget updated successfully") {
updateObjectPosition(zoneName, draggingIndex.index, boundedPosition); updateObjectPosition(zoneName, draggingIndex.index, boundedPosition);
} }
// Clean up // // 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) {
console.error("Error in handlePointerUp:", 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 +467,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 +476,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 +526,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 +535,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
@ -403,10 +554,13 @@ const DroppedObjects: React.FC = () => {
</div> </div>
<div className="label">Duplicate</div> <div className="label">Duplicate</div>
</div> </div>
<div className="edit btn" onClick={(event) => { <div
event.stopPropagation(); className="edit btn"
handleDelete(zoneName, obj.id, index); // Call the delete handler onClick={(event) => {
}}> event.stopPropagation();
handleDelete(zoneName, obj.id, index); // Call the delete handler
}}
>
<div className="icon"> <div className="icon">
<DeleteIcon /> <DeleteIcon />
</div> </div>
@ -414,11 +568,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
@ -451,4 +607,4 @@ const DroppedObjects: React.FC = () => {
export default DroppedObjects; export default DroppedObjects;
// always place the mousePointer in the same point in the floatingCard even in pointerMove

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,24 +144,39 @@ 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,
}; };
try { let addWidget = {
let response = await addingWidgets(selectedZone.zoneId, organization, newWidget); organization: organization,
zoneId: selectedZone.zoneId,
if (response.message === "Widget created successfully") { widget: newWidget
setSelectedZone((prev) => ({
...prev,
widgets: [...prev.widgets, newWidget],
}));
}
} catch (error) {
console.error("Error adding widget:", error);
} }
console.log('newWidget: ', newWidget);
console.log('addWidget: ', addWidget);
if (visualizationSocket) {
visualizationSocket.emit("v2:viz-widget:add", addWidget)
}
setSelectedZone((prev) => ({
...prev,
widgets: [...prev.widgets, newWidget],
}));
// try {
// let response = await addingWidgets(selectedZone.zoneId, organization, newWidget);
// if (response.message === "Widget created successfully") {
// setSelectedZone((prev) => ({
// ...prev,
// widgets: [...prev.widgets, newWidget],
// }));
// }
// } catch (error) {
// console.error("Error adding widget:", error);
// }
}; };
@ -191,7 +209,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 +235,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,12 +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( let response = await addingFloatingWidgets(
selectedZone.zoneId, selectedZone.zoneId,
@ -174,7 +182,7 @@ const RealTimeVisulization: React.FC = () => {
], ],
}, },
})); }));
} catch (error) { } } catch (error) {}
}; };
return ( return (
@ -188,6 +196,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,6 +224,7 @@ const RealTimeVisulization: React.FC = () => {
<Scene /> <Scene />
</div> </div>
{activeModule === "visualization" && selectedZone.zoneName !== "" && <DroppedObjects />} {activeModule === "visualization" && selectedZone.zoneName !== "" && <DroppedObjects />}
{activeModule === "visualization" && <SocketRealTimeViz />}
{/* <DroppedObjects /> */} {/* <DroppedObjects /> */}
{activeModule === "visualization" && ( {activeModule === "visualization" && (
<> <>

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

@ -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 //////////
@ -118,6 +120,8 @@ 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();
@ -202,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();

View File

@ -0,0 +1,65 @@
import { useEffect } from "react";
import { useSocketStore } from "../../store/store";
import { useSelectedZoneStore } from "../../store/useZoneStore";
export default function SocketRealTimeViz() {
const { visualizationSocket } = useSocketStore();
const { selectedZone, setSelectedZone } = useSelectedZoneStore();
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) => {
if (addPanel.success) {
let addPanelData = addPanel.data.data
setSelectedZone(addPanelData)
}
})
//delete panel response
visualizationSocket.on("viz-panel:response:delete", (deletePanel: any) => {
if (deletePanel.success) {
let deletePanelData = deletePanel.data.data
setSelectedZone(deletePanelData)
}
})
// add 2dWidget
visualizationSocket.on("viz-widget:response:updates", (response: any) => {
console.log('response: ', response);
if (response.success && response.data) {
setSelectedZone((prev) => {
const isWidgetAlreadyAdded = prev.widgets.some(
(widget) => widget.id === response.data.widgetData.id
);
if (isWidgetAlreadyAdded) return prev; // Prevent duplicate addition
return {
...prev,
zoneId: response.data.zoneId,
zoneName: response.data.zoneName,
widgets: [...prev.widgets, response.data.widgetData], // Append new widget
};
});
}
});
//delete 2D Widget
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
}));
}
});
}
}, [])
useEffect(() => {
}, [selectedZone])
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

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

@ -36,7 +36,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, index: number) => void; // Add this line
duplicateObject: (zoneName: string, index: number) => void; // Add this line duplicateObject: (zoneName: string, index: number) => void; // Add this line
}; };
@ -77,15 +77,16 @@ export const useDroppedObjectsStore = create<DroppedObjectsState>((set) => ({
}; };
}), }),
deleteObject: (zoneName: string, index: number) => deleteObject: (zoneName: string, id: string, index: number) =>
set((state) => { set((state) => {
const zone = state.zones[zoneName]; const zone = state.zones[zoneName];
console.log("zone: ", zone);
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
}, },
}, },
}; };

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