Merge branch 'main' into simulation-animation

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

View File

@ -1,5 +1,5 @@
import { Html } from "@react-three/drei";
import React from "react";
import React, { useEffect, useMemo, useState } from "react";
import { Bar } from "react-chartjs-2";
import {
Chart as ChartJS,
@ -11,6 +11,11 @@ import {
Legend,
TooltipItem, // Import TooltipItem for typing
} from "chart.js";
import { ThroughputIcon } from "../../../icons/3dChartIcons";
import { useWidgetStore } from "../../../../store/useWidgetStore";
import useChartStore from "../../../../store/useChartStore";
import axios from "axios";
import io from "socket.io-client";
// Register ChartJS components
ChartJS.register(
@ -22,12 +27,28 @@ ChartJS.register(
Legend
);
interface ProductionCapacityProps {
id: string;
type: string;
position: [number, number, number];
}
const ProductionCapacity : React.FC<ProductionCapacityProps> = ({ position }) => {
const ProductionCapacity : React.FC<ProductionCapacityProps> = ({ id, type, position }) => {
const { selectedChartId,setSelectedChartId } = useWidgetStore();
const { measurements: chartMeasurements, duration: chartDuration, name: widgetName } = useChartStore();
const [measurements, setmeasurements] = useState<any>({});
const [duration, setDuration] = useState("1h")
const [name, setName] = useState("Widget")
const [chartData, setChartData] = useState<{ labels: string[]; datasets: any[] }>({
labels: [],
datasets: [],
});
const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL;
const email = localStorage.getItem("email") || "";
const organization = email?.split("@")[1]?.split(".")[0]
// Chart data for a week
const chartData = {
const defaultChartData = {
labels: ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"], // Days of the week
datasets: [
{
@ -78,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 (
<Html position={[position[0], position[1], position[2]]}
scale={[0.5, 0.5, 0.5]}
transform
sprite>
<div className="productionCapacity-wrapper card">
<div className="productionCapacity-wrapper card"
onClick={
() => setSelectedChartId({
id: id,
type: type
})
}>
<div className="headeproductionCapacityr-wrapper">
<div className="header">Production Capacity</div>
<div className="production-capacity">
@ -104,7 +207,7 @@ const ProductionCapacity : React.FC<ProductionCapacityProps> = ({ position }) =>
</div>{" "}
<div className="bar-chart charts">
{/* Bar Chart */}
<Bar data={chartData} options={chartOptions} />
<Bar data={Object.keys(measurements).length > 0 ? chartData : defaultChartData } options={chartOptions} />
</div>
</div>
</Html>

View File

@ -1,5 +1,5 @@
import { Html } from "@react-three/drei";
import React from "react";
import React, { useEffect, useMemo, useState } from "react";
import { Line } from "react-chartjs-2";
import {
Chart as ChartJS,
@ -12,6 +12,10 @@ import {
ChartOptions,
} from "chart.js";
import { WavyIcon } from "../../../icons/3dChartIcons";
import { useWidgetStore } from "../../../../store/useWidgetStore";
import useChartStore from "../../../../store/useChartStore";
import axios from "axios";
import io from "socket.io-client";
// Register Chart.js components
ChartJS.register(
@ -36,9 +40,24 @@ const SmoothLineGraphComponent: React.FC<SmoothLineGraphProps> = ({
return <Line data={data} options={options} />;
};
interface ReturnOfInvestmentProps {
id: string;
type: string;
position: [number, number, number];
}
const ReturnOfInvestment: React.FC<ReturnOfInvestmentProps> = ({ position }) => {
const ReturnOfInvestment: React.FC<ReturnOfInvestmentProps> = ({ id, type, position }) => {
const { selectedChartId,setSelectedChartId } = useWidgetStore();
const { measurements: chartMeasurements, duration: chartDuration, name: widgetName } = useChartStore();
const [measurements, setmeasurements] = useState<any>({});
const [duration, setDuration] = useState("1h")
const [name, setName] = useState("Widget")
const [chartData, setChartData] = useState<{ labels: string[]; datasets: any[] }>({
labels: [],
datasets: [],
});
const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL;
const email = localStorage.getItem("email") || "";
const organization = email?.split("@")[1]?.split(".")[0]
// Improved sample data for the smooth curve graph (single day)
const graphData: ChartData<"line"> = {
labels: [
@ -107,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 (
<Html position={[position[0], position[1], position[2]]}
scale={[0.5, 0.5, 0.5]}
transform
zIndexRange={[1,0]}
sprite>
<div className="returnOfInvestment card">
<div className="returnOfInvestment card"
onClick={
() => setSelectedChartId({
id: id,
type: type
})
}>
<div className="header">Return of Investment</div>
<div className="lineGraph charts">
{/* Smooth curve graph with two datasets */}
<SmoothLineGraphComponent data={graphData} options={graphOptions} />
<SmoothLineGraphComponent data={Object.keys(measurements).length > 0 ? chartData : graphData} options={graphOptions} />
</div>
<div className="returns-wrapper">
<div className="icon">

View File

@ -1,28 +1,112 @@
import { Html } from "@react-three/drei";
import React, { useEffect, useMemo, useState } from "react";
import { useWidgetStore } from "../../../../store/useWidgetStore";
import useChartStore from "../../../../store/useChartStore";
import axios from "axios";
import io from "socket.io-client";
// import image from "../../../../assets/image/temp/image.png";
interface StateWorkingProps {
id:string;
type: string;
position: [number, number, number];
}
const StateWorking: React.FC<StateWorkingProps> = ({ position }) => {
const datas = [
{ key: "Oil Tank:", value: "24/341" },
{ key: "Oil Refin:", value: 36.023 },
{ key: "Transmission:", value: 36.023 },
{ key: "Fuel:", value: 36732 },
{ key: "Power:", value: 1300 },
{ key: "Time:", value: 13 - 9 - 2023 },
];
const StateWorking: React.FC<StateWorkingProps> = ({ id, type, position }) => {
const { selectedChartId, setSelectedChartId } = useWidgetStore();
const { measurements: chartMeasurements, duration: chartDuration, name: widgetName } = useChartStore();
const [measurements, setmeasurements] = useState<any>({});
const [duration, setDuration] = useState("1h")
const [name, setName] = useState("Widget")
const [datas, setDatas] = useState<any>({});
const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL;
const email = localStorage.getItem("email") || "";
const organization = email?.split("@")[1]?.split(".")[0]
// const datas = [
// { key: "Oil Tank:", value: "24/341" },
// { key: "Oil Refin:", value: 36.023 },
// { key: "Transmission:", value: 36.023 },
// { key: "Fuel:", value: 36732 },
// { key: "Power:", value: 1300 },
// { key: "Time:", value: 13 - 9 - 2023 },
// ];
useEffect(() => {
if (!iotApiUrl || !measurements || Object.keys(measurements).length === 0) return;
const socket = io(`http://${iotApiUrl}`);
const inputData = {
measurements,
duration,
interval: 1000,
};
const startStream = () => {
socket.emit("lastInput", inputData);
};
socket.on("connect", startStream);
socket.on("lastOutput", (response) => {
const responseData = response;
console.log("responceeeeeeeeeee",response);
setDatas(responseData);
});
return () => {
socket.off("lastOutput");
socket.emit("stop_stream"); // Stop streaming when component unmounts
socket.disconnect();
};
}, [measurements, duration, iotApiUrl]);
const fetchSavedInputes = async() => {
if (id !== "") {
try {
const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/widget3D/${id}/${organization}`);
if (response.status === 200) {
setmeasurements(response.data.Data.measurements)
setDuration(response.data.Data.duration)
setName(response.data.widgetName)
} else {
console.log("Unexpected response:", response);
}
} catch (error) {
console.error("There was an error!", error);
}
}
}
console.log("dataaaaa",datas);
useEffect(() => {
fetchSavedInputes();
}, []);
useEffect(() => {
if (selectedChartId?.id === id) {
fetchSavedInputes();
}
}
,[chartMeasurements, chartDuration, widgetName])
return (
<Html position={[position[0], position[1], position[2]]}
scale={[0.5, 0.5, 0.5]}
transform
zIndexRange={[1,0]}
sprite>
<div className="stateWorking-wrapper card">
<div className="stateWorking-wrapper card"
onClick={
() => setSelectedChartId({
id: id,
type: type
})
}>
<div className="header-wrapper">
<div className="header">
<span>State</span>
<span>
Working <span>.</span>
{datas?.input1 ? datas.input1 : 'input1'} <span>.</span>
</span>
</div>
<div className="img">
@ -31,12 +115,36 @@ const StateWorking: React.FC<StateWorkingProps> = ({ position }) => {
</div>
{/* Data */}
<div className="data-wrapper">
{datas.map((data, index) => (
{/* {datas.map((data, index) => (
<div className="data-table" key={index}>
<div className="data">{data.key}</div>
<div className="key">{data.value}</div>
</div>
))}
))} */}
<div className="data-table">
<div className="data">{measurements?.input2?.fields ? measurements.input2.fields : 'input2'}</div>
<div className="key">{datas?.input2 ? datas.input2 : 'data'}</div>
</div>
<div className="data-table">
<div className="data">{measurements?.input3?.fields ? measurements.input3.fields : 'input3'}</div>
<div className="key">{datas?.input3 ? datas.input3 : 'data'}</div>
</div>
<div className="data-table">
<div className="data">{measurements?.input4?.fields ? measurements.input4.fields : 'input4'}</div>
<div className="key">{datas?.input4 ? datas.input4 : 'data'}</div>
</div>
<div className="data-table">
<div className="data">{measurements?.input5?.fields ? measurements.input5.fields : 'input5'}</div>
<div className="key">{datas?.input5 ? datas.input5 : 'data'}</div>
</div>
<div className="data-table">
<div className="data">{measurements?.input6?.fields ? measurements.input6.fields : 'input6'}</div>
<div className="key">{datas?.input6 ? datas.input6 : 'data'}</div>
</div>
<div className="data-table">
<div className="data">{measurements?.input7?.fields ? measurements.input7.fields : 'input7'}</div>
<div className="key">{datas?.input7 ? datas.input7 : 'data'}</div>
</div>
</div>
</div>
</Html>

View File

@ -1,5 +1,5 @@
import { Html } from "@react-three/drei";
import React from "react";
import React, { useEffect, useMemo, useState } from "react";
import { Line } from "react-chartjs-2";
import {
Chart as ChartJS,
@ -14,6 +14,10 @@ import {
ChartOptions,
} from "chart.js";
import { ThroughputIcon } from "../../../icons/3dChartIcons";
import { useWidgetStore } from "../../../../store/useWidgetStore";
import useChartStore from "../../../../store/useChartStore";
import axios from "axios";
import io from "socket.io-client";
// Register Chart.js components
ChartJS.register(
@ -38,10 +42,25 @@ const LineGraphComponent: React.FC<LineGraphProps> = ({ data, options }) => {
};
interface ThroughputProps {
id: string;
type: string;
position: [number, number, number];
}
const Throughput: React.FC<ThroughputProps> = ({ position }) => {
const Throughput: React.FC<ThroughputProps> = ({ id, type, position }) => {
const { selectedChartId,setSelectedChartId } = useWidgetStore();
const { measurements: chartMeasurements, duration: chartDuration, name: widgetName } = useChartStore();
const [measurements, setmeasurements] = useState<any>({});
const [duration, setDuration] = useState("1h")
const [name, setName] = useState("Widget")
const [chartData, setChartData] = useState<{ labels: string[]; datasets: any[] }>({
labels: [],
datasets: [],
});
const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL;
const email = localStorage.getItem("email") || "";
const organization = email?.split("@")[1]?.split(".")[0]
// Sample data for the line graph
const graphData: ChartData<"line"> = {
@ -90,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 (
<Html position={[position[0], position[1], position[2]]}
scale={[0.5, 0.5, 0.5]}
transform
zIndexRange={[1, 0]}
sprite>
<div className="throughput-wrapper">
<div className="header">Throughput</div>
<div className="throughput-wrapper"
onClick={
() => setSelectedChartId({
id: id,
type: type
})
}>
<div className="header">{name}</div>
<div className="display-value">
<div className="left">
<div className="icon">
@ -119,7 +219,7 @@ const Throughput: React.FC<ThroughputProps> = ({ position }) => {
</div>
<div className="line-graph">
{/* Line graph using react-chartjs-2 */}
<LineGraphComponent data={graphData} options={graphOptions} />
<LineGraphComponent data={Object.keys(measurements).length > 0 ? chartData : graphData} options={graphOptions} />
</div>
<div className="footer">
You made an extra <span className="value">$1256.13</span> this month

View File

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

View File

@ -9,13 +9,14 @@ import { getCategoryAsset } from "../../../services/factoryBuilder/assest/assets
import arch from "../../../assets/gltf-glb/arch.glb";
import door from "../../../assets/gltf-glb/door.glb";
import window from "../../../assets/gltf-glb/window.glb";
import { fetchAssets } from "../../../services/marketplace/fetchAssets";
import { useSelectedItem } from "../../../store/store";
interface AssetProp {
filename: string;
thumbnail?: string;
category: string;
description?: string;
tags?: string;
tags: string;
url?: String;
uploadDate?: number;
isArchieve?: boolean;
@ -23,19 +24,58 @@ interface AssetProp {
price?: number;
CreatedBy?: String;
}
interface CategoryListProp {
assetImage?: string;
assetName?: string;
categoryImage: string;
category: string;
}
const Assets: React.FC = () => {
const { setSelectedItem } = useSelectedItem();
const [searchValue, setSearchValue] = useState<string>("");
const [selectedCategory, setSelectedCategory] = useState<string | null>(null);
const [filteredAsset, setFilteredAsset] = useState<AssetProp[]>([]);
const [categoryAssets, setCategoryAssets] = useState<AssetProp[]>([]);
const [filtereredAssets, setFiltereredAssets] = useState<AssetProp[]>([]);
const [categoryList, setCategoryList] = useState<CategoryListProp[]>([]);
const handleSearchChange = (value: string) => {
const searchTerm = value.toLowerCase();
setSearchValue(value);
setSelectedCategory(null); // Reset selected category when search changes
};
if (searchTerm.trim() === "" && !selectedCategory) {
setCategoryAssets([]);
return;
}
const filteredModels = filtereredAssets?.filter((model) => {
if (!model?.tags || !model?.filename || !model?.category) return false;
if (searchTerm.startsWith(":") && searchTerm.length > 1) {
const tagSearchTerm = searchTerm.slice(1);
return model.tags.toLowerCase().includes(tagSearchTerm);
} else if (selectedCategory) {
return (
model.category
.toLowerCase()
.includes(selectedCategory.toLowerCase()) &&
model.filename.toLowerCase().includes(searchTerm)
);
} else {
return model.filename.toLowerCase().includes(searchTerm);
}
});
const categoryList = useMemo(
() => [
setCategoryAssets(filteredModels);
};
useEffect(() => {
const filteredAssets = async () => {
try {
const filt = await fetchAssets();
setFiltereredAssets(filt);
} catch {}
};
filteredAssets();
}, [categoryAssets]);
useEffect(() => {
setCategoryList([
{
assetName: "Doors",
assetImage: "",
@ -58,10 +98,8 @@ const Assets: React.FC = () => {
{ category: "Workstation", categoryImage: workStation },
{ category: "Machines", categoryImage: machines },
{ category: "Workers", categoryImage: worker },
],
[]
);
]);
}, []);
const fetchCategoryAssets = async (asset: any) => {
setSelectedCategory(asset);
if (asset === "Feneration") {
@ -70,60 +108,93 @@ const Assets: React.FC = () => {
filename: "arch",
category: "Feneration",
url: arch,
tags: "arch",
},
{
filename: "door",
category: "Feneration",
url: door,
thumbnail: feneration,
tags: "door",
},
{
filename: "window",
category: "Feneration",
url: window,
tags: "window",
},
];
setFilteredAsset(localAssets);
setCategoryAssets(localAssets);
setFiltereredAssets(localAssets);
} else {
try {
const res = await getCategoryAsset(asset);
setFilteredAsset(res || []); // Ensure it's always an array
} catch (error) { }
setCategoryAssets(res);
setFiltereredAssets(res);
} catch (error) {}
}
};
useEffect(() => { }, [filteredAsset]);
return (
<div className="assets-container">
<Search onChange={handleSearchChange} />
{searchValue ? (
<div className="searched-content">
<p>Results for "{searchValue}"</p>
<div className="assets-result">
<div className="assets-wrapper">
<div className="searched-content">
<p>Results for {searchValue}</p>
</div>
</div>
<div className="assets-container">
{categoryAssets &&
categoryAssets?.map((asset: any, index: number) => (
<div key={index} className="assets">
<img
src={asset?.thumbnail}
alt={asset.filename}
className="asset-image"
/>
<div className="asset-name">
{asset.filename
.split("_")
.map(
(word: any) =>
word.charAt(0).toUpperCase() + word.slice(1)
)
.join(" ")}
</div>
</div>
))}
</div>
</div>
) : selectedCategory ? (
<div className="assets-wrapper">
{/* Back Button */}
<div
className="back-button"
onClick={() => {
setSelectedCategory(null);
setFilteredAsset([]);
setCategoryAssets([]);
}}
>
Back
</div>
<h2>{selectedCategory}</h2>
<div className="assets-container">
{filteredAsset &&
filteredAsset?.map((asset: any, index: number) => (
{categoryAssets &&
categoryAssets?.map((asset: any, index: number) => (
<div key={index} className="assets">
{asset?.thumbnail && (
<img
src={asset?.thumbnail}
alt={asset.filename}
className="asset-image"
onPointerDown={() => setSelectedItem({ name: asset.filename, id: asset.modelfileID })}
/>
)}
<img
src={asset?.thumbnail}
alt={asset.filename}
className="asset-image"
onPointerDown={() =>
setSelectedItem({
name: asset.filename,
id: asset.modelfileID,
})
}
/>
<div className="asset-name">
{asset.filename
.split("_")

View File

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

View File

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

View File

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

View File

@ -1,4 +1,4 @@
import React, { useState } from "react";
import React, { useEffect, useState } from "react";
import InputRange from "../../../ui/inputs/InputRange";
import InputToggle from "../../../ui/inputs/InputToggle";
import { AI_Icon } from "../../../icons/ExportCommonIcons";
@ -6,17 +6,20 @@ import LabeledButton from "../../../ui/inputs/LabledButton";
import {
useAzimuth,
useElevation,
useLimitDistance,
useRenderDistance,
useResetCamera,
useRoofVisibility,
useSelectedWallItem,
useShadows,
useSocketStore,
useTileDistance,
useToggleView,
useWallVisibility,
} from "../../../../store/store";
import { setEnvironment } from "../../../../services/factoryBuilder/environment/setEnvironment";
import * as CONSTANTS from "../../../../types/world/worldConstants";
import { validateBBox } from "@turf/helpers";
const GlobalProperties: React.FC = () => {
const { toggleView, setToggleView } = useToggleView();
const { selectedWallItem, setSelectedWallItem } = useSelectedWallItem();
@ -27,26 +30,95 @@ const GlobalProperties: React.FC = () => {
const { elevation, setElevation } = useElevation();
const { azimuth, setAzimuth } = useAzimuth();
const { renderDistance, setRenderDistance } = useRenderDistance();
const { setPlaneValue, setGridValue, planeValue, gridValue } =
useTileDistance();
useEffect(() => {
console.log(gridValue, planeValue, "values");
}, [gridValue, planeValue]);
const { socket } = useSocketStore();
const [limitDistance, setLimitDistance] = useState(false);
const [distance, setDistance] = useState<number>(30);
const { limitDistance, setLimitDistance } = useLimitDistance();
const [distance, setDistance] = useState<number>(40);
useEffect(() => {}, [limitDistance]);
const [limitGridDistance, setLimitGridDistance] = useState(false);
const [gridDistance, setGridDistance] = useState<number>(5);
const [gridDistance, setGridDistance] = useState<number>(3);
function optimizeScene() {
const optimizeScene = async (value: any) => {
const email = localStorage.getItem("email");
const organization = email?.split("@")[1]?.split(".")[0] || "defaultOrg";
const data = await setEnvironment(
organization,
localStorage.getItem("userId")!,
wallVisibility,
roofVisibility,
shadows,
30,
true
);
setRenderDistance(30);
setLimitDistance(true);
setDistance(30);
}
};
const limitRenderDistance = async () => {
const email = localStorage.getItem("email");
const organization = email?.split("@")[1]?.split(".")[0] || "defaultOrg";
if (limitDistance) {
let data = await setEnvironment(
organization,
localStorage.getItem("userId")!,
wallVisibility,
roofVisibility,
shadows,
75,
!limitDistance
);
setRenderDistance(75);
} else {
let data = await setEnvironment(
organization,
localStorage.getItem("userId")!,
wallVisibility,
roofVisibility,
shadows,
renderDistance,
!limitDistance
);
}
setLimitDistance(!limitDistance);
};
function updateDistance(value: number) {
setDistance(value);
setRenderDistance(value);
}
function updateGridDistance(value: number) {
setGridDistance(value);
// setGridValue({ size: value * 100, divisions: (value * 100) / 4 });
// setPlaneValue({ height: value * 100, width: value * 100 });
}
function updatedGrid(value: number) {
console.log(" (value * 100) / 4 : ", (value * 100) / 4);
setGridValue({ size: value * 100, divisions: (value * 100) / 4 });
setPlaneValue({ height: value * 100, width: value * 100 });
}
const updatedDist = async (value: number) => {
const email = localStorage.getItem("email");
const organization = email?.split("@")[1]?.split(".")[0] || "defaultOrg";
setRenderDistance(value);
// setDistance(value);
const data = await setEnvironment(
organization,
localStorage.getItem("userId")!,
wallVisibility,
roofVisibility,
shadows,
value,
limitDistance
);
};
// Function to toggle roof visibility
const changeRoofVisibility = async () => {
const email = localStorage.getItem("email");
@ -58,7 +130,9 @@ const GlobalProperties: React.FC = () => {
localStorage.getItem("userId")!,
wallVisibility,
!roofVisibility,
shadows
shadows,
renderDistance,
limitDistance
);
//
@ -85,7 +159,9 @@ const GlobalProperties: React.FC = () => {
localStorage.getItem("userId")!,
!wallVisibility,
roofVisibility,
shadows
shadows,
renderDistance,
limitDistance
);
//
@ -112,7 +188,9 @@ const GlobalProperties: React.FC = () => {
localStorage.getItem("userId")!,
wallVisibility,
roofVisibility,
!shadows
!shadows,
renderDistance,
limitDistance
);
//
@ -184,18 +262,24 @@ const GlobalProperties: React.FC = () => {
inputKey="4"
label="Limit Render Distance"
value={limitDistance}
onClick={() => {
setLimitDistance(!limitDistance);
// onClick={() => {
// setLimitDistance(!limitDistance);
// // setDistance(75);
// // setRenderDistance(75);
// }}
onClick={async () => {
await limitRenderDistance(); // Call the function here
}}
/>
<InputRange
label="Distance"
disabled={!limitDistance}
value={distance}
key={"5"}
value={renderDistance}
min={CONSTANTS.distanceConfig.minDistance}
max={CONSTANTS.distanceConfig.maxDistance}
onChange={(value: number) => updateDistance(value)}
onPointerUp={updatedDist}
key={"6"}
/>
<div className="split"></div>
@ -213,7 +297,10 @@ const GlobalProperties: React.FC = () => {
disabled={!limitGridDistance}
value={gridDistance}
key={"7"}
min={1}
max={5}
onChange={(value: number) => updateGridDistance(value)}
onPointerUp={updatedGrid}
/>
</div>
);

View File

@ -0,0 +1,177 @@
import React, { useEffect, useState } from "react";
import MultiLevelDropdown from "../../../../ui/inputs/MultiLevelDropDown";
import { AddIcon } from "../../../../icons/ExportCommonIcons";
import RegularDropDown from "../../../../ui/inputs/RegularDropDown";
import useChartStore from "../../../../../store/useChartStore";
import { useSelectedZoneStore } from "../../../../../store/useZoneStore";
import { useWidgetStore } from "../../../../../store/useWidgetStore";
import axios from "axios";
import RenameInput from "../../../../ui/inputs/RenameInput";
type Props = {};
const BarChartInput = (props: Props) => {
const [widgetName, setWidgetName] = useState('Widget');
const { setMeasurements, updateDuration, updateName } = useChartStore();
const [duration, setDuration] = useState('1h')
const [dropDowndata, setDropDownData] = useState({});
const [selections, setSelections] = useState<Record<string, { name: string; fields: string }>>({});
const { selectedZone } = useSelectedZoneStore();
const { selectedChartId } = useWidgetStore();
const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL;
const email = localStorage.getItem("email") || "";
const organization = email?.split("@")[1]?.split(".")[0]
useEffect(() => {
const fetchZoneData = async () => {
try {
const response = await axios.get(`http://${iotApiUrl}/getinput`);
if (response.status === 200) {
// console.log("dropdown data:", response.data);
setDropDownData(response.data);
} else {
console.log("Unexpected response:", response);
}
} catch (error) {
console.error("There was an error!", error);
}
};
fetchZoneData();
}, []);
useEffect(() => {
const fetchSavedInputes = async () => {
if (selectedChartId.id !== "") {
try {
const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/WidgetData/${selectedChartId.id}/${organization}`);
if (response.status === 200) {
setSelections(response.data.Data.measurements)
setDuration(response.data.Data.duration)
setWidgetName(response.data.widgetName)
} else {
console.log("Unexpected response:", response);
}
} catch (error) {
console.error("There was an error!", error);
}
}
}
fetchSavedInputes();
}, [selectedChartId.id]);
// Sync Zustand state when component mounts
useEffect(() => {
setMeasurements(selections);
updateDuration(duration);
updateName(widgetName);
}, [selections, duration, widgetName]);
const sendInputes = async (inputMeasurement: any, inputDuration: any, inputName: any) => {
try {
const response = await axios.post(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/widget/save`, {
organization: organization,
zoneId: selectedZone.zoneId,
widget: {
id: selectedChartId.id,
panel: selectedChartId.panel,
widgetName: inputName,
Data: {
measurements: inputMeasurement,
duration: inputDuration
}
}
} as any);
if (response.status === 200) {
return true
} else {
console.log("Unexpected response:", response);
return false
}
} catch (error) {
console.error("There was an error!", error);
return false
}
}
const handleSelect = async (inputKey: string, selectedData: { name: string; fields: string } | null) => {
// async() => {
const newSelections = { ...selections };
if (selectedData === null) {
delete newSelections[inputKey];
} else {
newSelections[inputKey] = selectedData;
}
// setMeasurements(newSelections); // Update Zustand store
console.log(newSelections);
if (await sendInputes(newSelections, duration, widgetName)) {
setSelections(newSelections);
}
// sendInputes(newSelections, duration); // Send data to server
// return newSelections;
// };
};
const handleSelectDuration = async (option: string) => {
if (await sendInputes(selections, option, widgetName)) {
setDuration(option);
}
// setDuration(option);
};
const handleNameChange = async (name:any) => {
console.log('name change requested',name);
if (await sendInputes(selections, duration, name)) {
setWidgetName(name);
}
}
return (
<>
<div className="inputs-wrapper">
<div className="datas">
<div className="datas__label">Title</div>
<RenameInput value={selectedChartId?.title || "untited"} onRename={handleNameChange}/>
</div>
{[...Array(3)].map((_, index) => {
const inputKey = `input${index + 1}`;
return (
<div key={index} className="datas">
<div className="datas__label">Input {index + 1}</div>
<div className="datas__class">
<MultiLevelDropdown
data={dropDowndata}
onSelect={(selectedData) => handleSelect(inputKey, selectedData)}
onUnselect={() => handleSelect(inputKey, null)}
selectedValue={selections[inputKey]} // Load from Zustand
/>
<div className="icon">
<AddIcon />
</div>
</div>
</div>
);
})}
</div>
<div>
<div className="datas">
<div className="datas__label">Duration</div>
<div className="datas__class">
<RegularDropDown
header={duration}
options={["1h", "2h", "12h"]}
onSelect={handleSelectDuration}
search={false}
/>
</div>
</div>
</div>
</>
);
};
export default BarChartInput;

View File

@ -0,0 +1,178 @@
import React, { useEffect, useState } from "react";
import MultiLevelDropdown from "../../../../ui/inputs/MultiLevelDropDown";
import { AddIcon } from "../../../../icons/ExportCommonIcons";
import RegularDropDown from "../../../../ui/inputs/RegularDropDown";
import useChartStore from "../../../../../store/useChartStore";
import { useSelectedZoneStore } from "../../../../../store/useZoneStore";
import { useWidgetStore } from "../../../../../store/useWidgetStore";
import axios from "axios";
import RenameInput from "../../../../ui/inputs/RenameInput";
type Props = {};
const FleetEfficiencyInputComponent = (props: Props) => {
const [widgetName, setWidgetName] = useState('Widget');
const { setFlotingMeasurements, updateFlotingDuration, updateHeader } = useChartStore();
const [duration, setDuration] = useState('1h')
const [dropDowndata, setDropDownData] = useState({});
const [selections, setSelections] = useState<Record<string, { name: string; fields: string }>>({});
const { selectedZone } = useSelectedZoneStore();
const { selectedChartId } = useWidgetStore();
const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL;
const email = localStorage.getItem("email") || "";
const organization = email?.split("@")[1]?.split(".")[0]
useEffect(() => {
const fetchZoneData = async () => {
try {
const response = await axios.get(`http://${iotApiUrl}/getinput`);
if (response.status === 200) {
// console.log("dropdown data:", response.data);
setDropDownData(response.data);
} else {
console.log("Unexpected response:", response);
}
} catch (error) {
console.error("There was an error!", error);
}
};
fetchZoneData();
}, []);
useEffect(() => {
const fetchSavedInputes = async () => {
if (selectedChartId.id !== "") {
try {
const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/A_floatWidget/${selectedChartId.id}/${organization}`);
if (response.status === 200) {
console.log(response.data);
setSelections(response.data.Data.measurements)
setDuration(response.data.Data.duration)
setWidgetName(response.data.header)
} else {
console.log("Unexpected response:", response);
}
} catch (error) {
console.error("There was an error!", error);
}
}
}
fetchSavedInputes();
}, [selectedChartId.id]);
// Sync Zustand state when component mounts
useEffect(() => {
setFlotingMeasurements(selections);
updateFlotingDuration(duration);
updateHeader(widgetName);
}, [selections, duration, widgetName]);
const sendInputes = async (inputMeasurement: any, inputDuration: any, inputName: any) => {
try {
const response = await axios.post(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/floatwidget/save`, {
organization: organization,
zoneId: selectedZone.zoneId,
widget: {
id: selectedChartId.id,
header: inputName,
Data: {
measurements: inputMeasurement,
duration: inputDuration
}
}
} as any);
if (response.status === 200) {
return true
} else {
console.log("Unexpected response:", response);
return false
}
} catch (error) {
console.error("There was an error!", error);
return false
}
}
const handleSelect = async (inputKey: string, selectedData: { name: string; fields: string } | null) => {
// async() => {
const newSelections = { ...selections };
if (selectedData === null) {
delete newSelections[inputKey];
} else {
newSelections[inputKey] = selectedData;
}
// setMeasurements(newSelections); // Update Zustand store
console.log(newSelections);
if (await sendInputes(newSelections, duration, widgetName)) {
setSelections(newSelections);
}
// sendInputes(newSelections, duration); // Send data to server
// return newSelections;
// };
};
const handleSelectDuration = async (option: string) => {
if (await sendInputes(selections, option, widgetName)) {
setDuration(option);
}
// setDuration(option);
};
const handleNameChange = async (name:any) => {
console.log('name change requested',name);
if (await sendInputes(selections, duration, name)) {
setWidgetName(name);
}
}
return (
<>
<div className="inputs-wrapper">
<div className="datas">
<div className="datas__label">Title</div>
<RenameInput value={selectedChartId?.header || "untited"} onRename={handleNameChange}/>
</div>
{[...Array(1)].map((_, index) => {
const inputKey = `input${index + 1}`;
return (
<div key={index} className="datas">
<div className="datas__label">Input {index + 1}</div>
<div className="datas__class">
<MultiLevelDropdown
data={dropDowndata}
onSelect={(selectedData) => handleSelect(inputKey, selectedData)}
onUnselect={() => handleSelect(inputKey, null)}
selectedValue={selections[inputKey]} // Load from Zustand
/>
<div className="icon">
<AddIcon />
</div>
</div>
</div>
);
})}
</div>
<div>
{/* <div className="datas">
<div className="datas__label">Duration</div>
<div className="datas__class">
<RegularDropDown
header={duration}
options={["1h", "2h", "12h"]}
onSelect={handleSelectDuration}
search={false}
/>
</div>
</div> */}
</div>
</>
);
};
export default FleetEfficiencyInputComponent;

View File

@ -12,7 +12,7 @@ type Props = {};
const FlotingWidgetInput = (props: Props) => {
const [widgetName, setWidgetName] = useState('Widget');
const { setMeasurements, updateDuration, updateName } = useChartStore();
const { setFlotingMeasurements, updateFlotingDuration, updateHeader } = useChartStore();
const [duration, setDuration] = useState('1h')
const [dropDowndata, setDropDownData] = useState({});
const [selections, setSelections] = useState<Record<string, { name: string; fields: string }>>({});
@ -43,11 +43,13 @@ const FlotingWidgetInput = (props: Props) => {
const fetchSavedInputes = async () => {
if (selectedChartId.id !== "") {
try {
const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/WidgetData/${selectedChartId.id}/${organization}`);
const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/A_floatWidget/${selectedChartId.id}/${organization}`);
if (response.status === 200) {
console.log(response.data);
setSelections(response.data.Data.measurements)
setDuration(response.data.Data.duration)
setWidgetName(response.data.widgetName)
setWidgetName(response.data.header)
} else {
console.log("Unexpected response:", response);
}
@ -63,9 +65,9 @@ const FlotingWidgetInput = (props: Props) => {
// Sync Zustand state when component mounts
useEffect(() => {
setMeasurements(selections);
updateDuration(duration);
updateName(widgetName);
setFlotingMeasurements(selections);
updateFlotingDuration(duration);
updateHeader(widgetName);
}, [selections, duration, widgetName]);
@ -76,8 +78,7 @@ const FlotingWidgetInput = (props: Props) => {
zoneId: selectedZone.zoneId,
widget: {
id: selectedChartId.id,
panel: selectedChartId.panel,
widgetName: inputName,
header: inputName,
Data: {
measurements: inputMeasurement,
duration: inputDuration
@ -135,7 +136,7 @@ const FlotingWidgetInput = (props: Props) => {
<div className="inputs-wrapper">
<div className="datas">
<div className="datas__label">Title</div>
<RenameInput value={selectedChartId?.title || "untited"} onRename={handleNameChange}/>
<RenameInput value={selectedChartId?.header || "untited"} onRename={handleNameChange}/>
</div>
{[...Array(6)].map((_, index) => {
const inputKey = `input${index + 1}`;

View File

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

View File

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

View File

@ -1,78 +1,177 @@
import React, { useEffect, useState } from 'react'
import MultiLevelDropdown from '../../../../ui/inputs/MultiLevelDropDown'
import { AddIcon } from '../../../../icons/ExportCommonIcons'
import axios from 'axios'
import React, { useEffect, useState } from "react";
import MultiLevelDropdown from "../../../../ui/inputs/MultiLevelDropDown";
import { AddIcon } from "../../../../icons/ExportCommonIcons";
import RegularDropDown from "../../../../ui/inputs/RegularDropDown";
import useChartStore from "../../../../../store/useChartStore";
import { useSelectedZoneStore } from "../../../../../store/useZoneStore";
import { useWidgetStore } from "../../../../../store/useWidgetStore";
import axios from "axios";
import RenameInput from "../../../../ui/inputs/RenameInput";
type Props = {}
type Props = {};
const PieChartInput = (props: Props) => {
const [dropDowndata, setDropDownData] = useState({})
const [selections, setSelections] = useState<Record<string, {name: string, fields: string}>>({})
const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL;
const [widgetName, setWidgetName] = useState('Widget');
const { setMeasurements, updateDuration, updateName } = useChartStore();
const [duration, setDuration] = useState('1h')
const [dropDowndata, setDropDownData] = useState({});
const [selections, setSelections] = useState<Record<string, { name: string; fields: string }>>({});
const { selectedZone } = useSelectedZoneStore();
const { selectedChartId } = useWidgetStore();
const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL;
const email = localStorage.getItem("email") || "";
const organization = email?.split("@")[1]?.split(".")[0]
useEffect(() => {
const fetchZoneData = async () => {
try {
const response = await axios.get(`http://${iotApiUrl}/getinput`);
if (response.status === 200) {
console.log('dropdown data:', response.data);
setDropDownData(response.data)
} else {
console.log('Unexpected response:', response);
}
} catch (error) {
console.error('There was an error!', error);
}
};
fetchZoneData();
}, []);
useEffect(() => {console.log(selections);
},[selections])
const handleSelect = (inputKey: string, selectedData: {name: string, fields: string} | null) => {
setSelections(prev => {
if (selectedData === null) {
const newSelections = {...prev};
delete newSelections[inputKey];
return newSelections;
} else {
return {
...prev,
[inputKey]: selectedData
};
}
});
useEffect(() => {
const fetchZoneData = async () => {
try {
const response = await axios.get(`http://${iotApiUrl}/getinput`);
if (response.status === 200) {
// console.log("dropdown data:", response.data);
setDropDownData(response.data);
} else {
console.log("Unexpected response:", response);
}
} catch (error) {
console.error("There was an error!", error);
}
};
fetchZoneData();
}, []);
return (
<>
<div className="inputs-wrapper">
{[...Array(3)].map((_, index) => {
const inputKey = `input${index+1}`;
return (
<div key={index} className="datas">
<div className="datas__label">Input {index+1}</div>
<div className="datas__class">
<MultiLevelDropdown
data={dropDowndata}
onSelect={(selectedData) => handleSelect(inputKey, selectedData)}
onUnselect={() => handleSelect(inputKey, null)}
selectedValue={selections[inputKey]}
/>
<div className="icon">
<AddIcon />
</div>
</div>
</div>
);
})}
</div>
<div>
</div>
</>
)
}
useEffect(() => {
const fetchSavedInputes = async () => {
if (selectedChartId.id !== "") {
try {
const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/WidgetData/${selectedChartId.id}/${organization}`);
if (response.status === 200) {
setSelections(response.data.Data.measurements)
setDuration(response.data.Data.duration)
setWidgetName(response.data.widgetName)
} else {
console.log("Unexpected response:", response);
}
} catch (error) {
console.error("There was an error!", error);
}
}
}
export default PieChartInput
fetchSavedInputes();
}, [selectedChartId.id]);
// Sync Zustand state when component mounts
useEffect(() => {
setMeasurements(selections);
updateDuration(duration);
updateName(widgetName);
}, [selections, duration, widgetName]);
const sendInputes = async (inputMeasurement: any, inputDuration: any, inputName: any) => {
try {
const response = await axios.post(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/widget/save`, {
organization: organization,
zoneId: selectedZone.zoneId,
widget: {
id: selectedChartId.id,
panel: selectedChartId.panel,
widgetName: inputName,
Data: {
measurements: inputMeasurement,
duration: inputDuration
}
}
} as any);
if (response.status === 200) {
return true
} else {
console.log("Unexpected response:", response);
return false
}
} catch (error) {
console.error("There was an error!", error);
return false
}
}
const handleSelect = async (inputKey: string, selectedData: { name: string; fields: string } | null) => {
// async() => {
const newSelections = { ...selections };
if (selectedData === null) {
delete newSelections[inputKey];
} else {
newSelections[inputKey] = selectedData;
}
// setMeasurements(newSelections); // Update Zustand store
console.log(newSelections);
if (await sendInputes(newSelections, duration, widgetName)) {
setSelections(newSelections);
}
// sendInputes(newSelections, duration); // Send data to server
// return newSelections;
// };
};
const handleSelectDuration = async (option: string) => {
if (await sendInputes(selections, option, widgetName)) {
setDuration(option);
}
// setDuration(option);
};
const handleNameChange = async (name:any) => {
console.log('name change requested',name);
if (await sendInputes(selections, duration, name)) {
setWidgetName(name);
}
}
return (
<>
<div className="inputs-wrapper">
<div className="datas">
<div className="datas__label">Title</div>
<RenameInput value={selectedChartId?.title || "untited"} onRename={handleNameChange}/>
</div>
{[...Array(2)].map((_, index) => {
const inputKey = `input${index + 1}`;
return (
<div key={index} className="datas">
<div className="datas__label">Input {index + 1}</div>
<div className="datas__class">
<MultiLevelDropdown
data={dropDowndata}
onSelect={(selectedData) => handleSelect(inputKey, selectedData)}
onUnselect={() => handleSelect(inputKey, null)}
selectedValue={selections[inputKey]} // Load from Zustand
/>
<div className="icon">
<AddIcon />
</div>
</div>
</div>
);
})}
</div>
<div>
<div className="datas">
<div className="datas__label">Duration</div>
<div className="datas__class">
<RegularDropDown
header={duration}
options={["1h", "2h", "12h"]}
onSelect={handleSelectDuration}
search={false}
/>
</div>
</div>
</div>
</>
);
};
export default PieChartInput;

View File

@ -0,0 +1,171 @@
import React, { useEffect, useState } from "react";
import MultiLevelDropdown from "../../../../ui/inputs/MultiLevelDropDown";
import { AddIcon } from "../../../../icons/ExportCommonIcons";
import RegularDropDown from "../../../../ui/inputs/RegularDropDown";
import useChartStore from "../../../../../store/useChartStore";
import { useSelectedZoneStore } from "../../../../../store/useZoneStore";
import { useWidgetStore } from "../../../../../store/useWidgetStore";
import axios from "axios";
import RenameInput from "../../../../ui/inputs/RenameInput";
type Props = {};
const Progress1Input = (props: Props) => {
const [widgetName, setWidgetName] = useState('Widget');
const { setMeasurements, updateDuration, updateName } = useChartStore();
const [duration, setDuration] = useState('1h')
const [dropDowndata, setDropDownData] = useState({});
const [selections, setSelections] = useState<Record<string, { name: string; fields: string }>>({});
const { selectedZone } = useSelectedZoneStore();
const { selectedChartId } = useWidgetStore();
const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL;
const email = localStorage.getItem("email") || "";
const organization = email?.split("@")[1]?.split(".")[0]
useEffect(() => {
const fetchZoneData = async () => {
try {
const response = await axios.get(`http://${iotApiUrl}/getinput`);
if (response.status === 200) {
// console.log("dropdown data:", response.data);
setDropDownData(response.data);
} else {
console.log("Unexpected response:", response);
}
} catch (error) {
console.error("There was an error!", error);
}
};
fetchZoneData();
}, []);
useEffect(() => {
const fetchSavedInputes = async () => {
if (selectedChartId.id !== "") {
try {
const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/WidgetData/${selectedChartId.id}/${organization}`);
if (response.status === 200) {
setSelections(response.data.Data.measurements)
setDuration(response.data.Data.duration)
setWidgetName(response.data.widgetName)
} else {
console.log("Unexpected response:", response);
}
} catch (error) {
console.error("There was an error!", error);
}
}
}
fetchSavedInputes();
}, [selectedChartId.id]);
// Sync Zustand state when component mounts
useEffect(() => {
setMeasurements(selections);
updateDuration(duration);
updateName(widgetName);
}, [selections, duration, widgetName]);
const sendInputes = async (inputMeasurement: any, inputDuration: any, inputName: any) => {
try {
const response = await axios.post(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/widget/save`, {
organization: organization,
zoneId: selectedZone.zoneId,
widget: {
id: selectedChartId.id,
panel: selectedChartId.panel,
widgetName: inputName,
Data: {
measurements: inputMeasurement,
duration: inputDuration
}
}
} as any);
if (response.status === 200) {
return true
} else {
console.log("Unexpected response:", response);
return false
}
} catch (error) {
console.error("There was an error!", error);
return false
}
}
const handleSelect = async (inputKey: string, selectedData: { name: string; fields: string } | null) => {
// async() => {
const newSelections = { ...selections };
if (selectedData === null) {
delete newSelections[inputKey];
} else {
newSelections[inputKey] = selectedData;
}
// setMeasurements(newSelections); // Update Zustand store
console.log(newSelections);
if (await sendInputes(newSelections, duration, widgetName)) {
setSelections(newSelections);
}
};
const handleSelectDuration = async (option: string) => {
if (await sendInputes(selections, option, widgetName)) {
setDuration(option);
}
};
const handleNameChange = async (name:any) => {
if (await sendInputes(selections, duration, name)) {
setWidgetName(name);
}
}
return (
<>
<div className="inputs-wrapper">
<div className="datas">
<div className="datas__label">Title</div>
<RenameInput value={selectedChartId?.title || "untited"} onRename={handleNameChange}/>
</div>
{[...Array(1)].map((_, index) => {
const inputKey = `input${index + 1}`;
return (
<div key={index} className="datas">
<div className="datas__label">Input {index + 1}</div>
<div className="datas__class">
<MultiLevelDropdown
data={dropDowndata}
onSelect={(selectedData) => handleSelect(inputKey, selectedData)}
onUnselect={() => handleSelect(inputKey, null)}
selectedValue={selections[inputKey]} // Load from Zustand
/>
<div className="icon">
<AddIcon />
</div>
</div>
</div>
);
})}
</div>
{/* <div>
<div className="datas">
<div className="datas__label">Duration</div>
<div className="datas__class">
<RegularDropDown
header={duration}
options={["1h", "2h", "12h"]}
onSelect={handleSelectDuration}
search={false}
/>
</div>
</div>
</div> */}
</>
);
};
export default Progress1Input;

View File

@ -0,0 +1,171 @@
import React, { useEffect, useState } from "react";
import MultiLevelDropdown from "../../../../ui/inputs/MultiLevelDropDown";
import { AddIcon } from "../../../../icons/ExportCommonIcons";
import RegularDropDown from "../../../../ui/inputs/RegularDropDown";
import useChartStore from "../../../../../store/useChartStore";
import { useSelectedZoneStore } from "../../../../../store/useZoneStore";
import { useWidgetStore } from "../../../../../store/useWidgetStore";
import axios from "axios";
import RenameInput from "../../../../ui/inputs/RenameInput";
type Props = {};
const Progress2Input = (props: Props) => {
const [widgetName, setWidgetName] = useState('Widget');
const { setMeasurements, updateDuration, updateName } = useChartStore();
const [duration, setDuration] = useState('1h')
const [dropDowndata, setDropDownData] = useState({});
const [selections, setSelections] = useState<Record<string, { name: string; fields: string }>>({});
const { selectedZone } = useSelectedZoneStore();
const { selectedChartId } = useWidgetStore();
const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL;
const email = localStorage.getItem("email") || "";
const organization = email?.split("@")[1]?.split(".")[0]
useEffect(() => {
const fetchZoneData = async () => {
try {
const response = await axios.get(`http://${iotApiUrl}/getinput`);
if (response.status === 200) {
// console.log("dropdown data:", response.data);
setDropDownData(response.data);
} else {
console.log("Unexpected response:", response);
}
} catch (error) {
console.error("There was an error!", error);
}
};
fetchZoneData();
}, []);
useEffect(() => {
const fetchSavedInputes = async () => {
if (selectedChartId.id !== "") {
try {
const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/WidgetData/${selectedChartId.id}/${organization}`);
if (response.status === 200) {
setSelections(response.data.Data.measurements)
setDuration(response.data.Data.duration)
setWidgetName(response.data.widgetName)
} else {
console.log("Unexpected response:", response);
}
} catch (error) {
console.error("There was an error!", error);
}
}
}
fetchSavedInputes();
}, [selectedChartId.id]);
// Sync Zustand state when component mounts
useEffect(() => {
setMeasurements(selections);
updateDuration(duration);
updateName(widgetName);
}, [selections, duration, widgetName]);
const sendInputes = async (inputMeasurement: any, inputDuration: any, inputName: any) => {
try {
const response = await axios.post(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/widget/save`, {
organization: organization,
zoneId: selectedZone.zoneId,
widget: {
id: selectedChartId.id,
panel: selectedChartId.panel,
widgetName: inputName,
Data: {
measurements: inputMeasurement,
duration: inputDuration
}
}
} as any);
if (response.status === 200) {
return true
} else {
console.log("Unexpected response:", response);
return false
}
} catch (error) {
console.error("There was an error!", error);
return false
}
}
const handleSelect = async (inputKey: string, selectedData: { name: string; fields: string } | null) => {
// async() => {
const newSelections = { ...selections };
if (selectedData === null) {
delete newSelections[inputKey];
} else {
newSelections[inputKey] = selectedData;
}
// setMeasurements(newSelections); // Update Zustand store
console.log(newSelections);
if (await sendInputes(newSelections, duration, widgetName)) {
setSelections(newSelections);
}
};
const handleSelectDuration = async (option: string) => {
if (await sendInputes(selections, option, widgetName)) {
setDuration(option);
}
};
const handleNameChange = async (name:any) => {
if (await sendInputes(selections, duration, name)) {
setWidgetName(name);
}
}
return (
<>
<div className="inputs-wrapper">
<div className="datas">
<div className="datas__label">Title</div>
<RenameInput value={selectedChartId?.title || "untited"} onRename={handleNameChange}/>
</div>
{[...Array(2)].map((_, index) => {
const inputKey = `input${index + 1}`;
return (
<div key={index} className="datas">
<div className="datas__label">Input {index + 1}</div>
<div className="datas__class">
<MultiLevelDropdown
data={dropDowndata}
onSelect={(selectedData) => handleSelect(inputKey, selectedData)}
onUnselect={() => handleSelect(inputKey, null)}
selectedValue={selections[inputKey]} // Load from Zustand
/>
<div className="icon">
<AddIcon />
</div>
</div>
</div>
);
})}
</div>
{/* <div>
<div className="datas">
<div className="datas__label">Duration</div>
<div className="datas__class">
<RegularDropDown
header={duration}
options={["1h", "2h", "12h"]}
onSelect={handleSelectDuration}
search={false}
/>
</div>
</div>
</div> */}
</>
);
};
export default Progress2Input;

View File

@ -0,0 +1,178 @@
import React, { useEffect, useState } from "react";
import MultiLevelDropdown from "../../../../ui/inputs/MultiLevelDropDown";
import { AddIcon } from "../../../../icons/ExportCommonIcons";
import RegularDropDown from "../../../../ui/inputs/RegularDropDown";
import useChartStore from "../../../../../store/useChartStore";
import { useSelectedZoneStore } from "../../../../../store/useZoneStore";
import { useWidgetStore } from "../../../../../store/useWidgetStore";
import axios from "axios";
import RenameInput from "../../../../ui/inputs/RenameInput";
type Props = {};
const WarehouseThroughputInputComponent = (props: Props) => {
const [widgetName, setWidgetName] = useState('Widget');
const { setFlotingMeasurements, updateFlotingDuration, updateHeader } = useChartStore();
const [duration, setDuration] = useState('1h')
const [dropDowndata, setDropDownData] = useState({});
const [selections, setSelections] = useState<Record<string, { name: string; fields: string }>>({});
const { selectedZone } = useSelectedZoneStore();
const { selectedChartId } = useWidgetStore();
const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL;
const email = localStorage.getItem("email") || "";
const organization = email?.split("@")[1]?.split(".")[0]
useEffect(() => {
const fetchZoneData = async () => {
try {
const response = await axios.get(`http://${iotApiUrl}/getinput`);
if (response.status === 200) {
// console.log("dropdown data:", response.data);
setDropDownData(response.data);
} else {
console.log("Unexpected response:", response);
}
} catch (error) {
console.error("There was an error!", error);
}
};
fetchZoneData();
}, []);
useEffect(() => {
const fetchSavedInputes = async () => {
if (selectedChartId.id !== "") {
try {
const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/A_floatWidget/${selectedChartId.id}/${organization}`);
if (response.status === 200) {
console.log(response.data);
setSelections(response.data.Data.measurements)
setDuration(response.data.Data.duration)
setWidgetName(response.data.header)
} else {
console.log("Unexpected response:", response);
}
} catch (error) {
console.error("There was an error!", error);
}
}
}
fetchSavedInputes();
}, [selectedChartId.id]);
// Sync Zustand state when component mounts
useEffect(() => {
setFlotingMeasurements(selections);
updateFlotingDuration(duration);
updateHeader(widgetName);
}, [selections, duration, widgetName]);
const sendInputes = async (inputMeasurement: any, inputDuration: any, inputName: any) => {
try {
const response = await axios.post(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/floatwidget/save`, {
organization: organization,
zoneId: selectedZone.zoneId,
widget: {
id: selectedChartId.id,
header: inputName,
Data: {
measurements: inputMeasurement,
duration: inputDuration
}
}
} as any);
if (response.status === 200) {
return true
} else {
console.log("Unexpected response:", response);
return false
}
} catch (error) {
console.error("There was an error!", error);
return false
}
}
const handleSelect = async (inputKey: string, selectedData: { name: string; fields: string } | null) => {
// async() => {
const newSelections = { ...selections };
if (selectedData === null) {
delete newSelections[inputKey];
} else {
newSelections[inputKey] = selectedData;
}
// setMeasurements(newSelections); // Update Zustand store
console.log(newSelections);
if (await sendInputes(newSelections, duration, widgetName)) {
setSelections(newSelections);
}
// sendInputes(newSelections, duration); // Send data to server
// return newSelections;
// };
};
const handleSelectDuration = async (option: string) => {
if (await sendInputes(selections, option, widgetName)) {
setDuration(option);
}
// setDuration(option);
};
const handleNameChange = async (name:any) => {
console.log('name change requested',name);
if (await sendInputes(selections, duration, name)) {
setWidgetName(name);
}
}
return (
<>
<div className="inputs-wrapper">
<div className="datas">
<div className="datas__label">Title</div>
<RenameInput value={selectedChartId?.header || "untited"} onRename={handleNameChange}/>
</div>
{[...Array(1)].map((_, index) => {
const inputKey = `input${index + 1}`;
return (
<div key={index} className="datas">
<div className="datas__label">Input {index + 1}</div>
<div className="datas__class">
<MultiLevelDropdown
data={dropDowndata}
onSelect={(selectedData) => handleSelect(inputKey, selectedData)}
onUnselect={() => handleSelect(inputKey, null)}
selectedValue={selections[inputKey]} // Load from Zustand
/>
<div className="icon">
<AddIcon />
</div>
</div>
</div>
);
})}
</div>
<div>
<div className="datas">
<div className="datas__label">Duration</div>
<div className="datas__class">
<RegularDropDown
header={duration}
options={["1h", "2h", "12h"]}
onSelect={handleSelectDuration}
search={false}
/>
</div>
</div>
</div>
</>
);
};
export default WarehouseThroughputInputComponent;

View File

@ -0,0 +1,165 @@
import React, { useEffect, useState } from "react";
import MultiLevelDropdown from "../../../../ui/inputs/MultiLevelDropDown";
import { AddIcon } from "../../../../icons/ExportCommonIcons";
import RegularDropDown from "../../../../ui/inputs/RegularDropDown";
import useChartStore from "../../../../../store/useChartStore";
import { useSelectedZoneStore } from "../../../../../store/useZoneStore";
import { useWidgetStore } from "../../../../../store/useWidgetStore";
import axios from "axios";
import RenameInput from "../../../../ui/inputs/RenameInput";
type Props = {};
const Widget2InputCard3D = (props: Props) => {
const { selectedChartId } = useWidgetStore();
const [widgetName, setWidgetName] = useState("untited");
const { setMeasurements, updateDuration, updateName } = useChartStore();
const [duration, setDuration] = useState('1h')
const [dropDowndata, setDropDownData] = useState({});
const [selections, setSelections] = useState<Record<string, { name: string; fields: string }>>({});
const { selectedZone } = useSelectedZoneStore();
const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL;
const email = localStorage.getItem("email") || "";
const organization = email?.split("@")[1]?.split(".")[0]
console.log(selectedChartId);
useEffect(() => {
const fetchZoneData = async () => {
try {
const response = await axios.get(`http://${iotApiUrl}/getinput`);
if (response.status === 200) {
// console.log("dropdown data:", response.data);
setDropDownData(response.data);
} else {
console.log("Unexpected response:", response);
}
} catch (error) {
console.error("There was an error!", error);
}
};
fetchZoneData();
}, []);
useEffect(() => {
const fetchSavedInputes = async () => {
if (selectedChartId.id !== "") {
try {
const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/widget3D/${selectedChartId.id}/${organization}`);
if (response.status === 200) {
setSelections(response.data.Data.measurements)
setDuration(response.data.Data.duration)
setWidgetName(response.data.widgetName)
} else {
console.log("Unexpected response:", response);
}
} catch (error) {
console.error("There was an error!", error);
}
}
}
fetchSavedInputes();
}, [selectedChartId.id]);
// Sync Zustand state when component mounts
useEffect(() => {
setMeasurements(selections);
updateDuration(duration);
updateName(widgetName);
}, [selections, duration, widgetName]);
const sendInputes = async (inputMeasurement: any, inputDuration: any, inputName: any) => {
try {
const response = await axios.post(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/3dwidget/save`, {
organization: organization,
zoneId: selectedZone.zoneId,
widget: {
id: selectedChartId.id,
widgetName: inputName,
Data: {
measurements: inputMeasurement,
duration: inputDuration
}
}
} as any);
if (response.status === 200) {
return true
} else {
console.log("Unexpected response:", response);
return false
}
} catch (error) {
console.error("There was an error!", error);
return false
}
}
const handleSelect = async (inputKey: string, selectedData: { name: string; fields: string } | null) => {
// async() => {
const newSelections = { ...selections };
if (selectedData === null) {
delete newSelections[inputKey];
} else {
newSelections[inputKey] = selectedData;
}
// setMeasurements(newSelections); // Update Zustand store
console.log(newSelections);
if (await sendInputes(newSelections, duration, widgetName)) {
setSelections(newSelections);
}
// sendInputes(newSelections, duration); // Send data to server
// return newSelections;
// };
};
const handleSelectDuration = async (option: string) => {
if (await sendInputes(selections, option, widgetName)) {
setDuration(option);
}
// setDuration(option);
};
const handleNameChange = async (name:any) => {
console.log('name change requested',name);
if (await sendInputes(selections, duration, name)) {
setWidgetName(name);
}
}
return (
<>
<div className="inputs-wrapper">
<div className="datas">
<div className="datas__label">Title</div>
<RenameInput value={widgetName} onRename={handleNameChange}/>
</div>
{[...Array(2)].map((_, index) => {
const inputKey = `input${index + 1}`;
return (
<div key={index} className="datas">
<div className="datas__label">Input {index + 1}</div>
<div className="datas__class">
<MultiLevelDropdown
data={dropDowndata}
onSelect={(selectedData) => handleSelect(inputKey, selectedData)}
onUnselect={() => handleSelect(inputKey, null)}
selectedValue={selections[inputKey]} // Load from Zustand
/>
<div className="icon">
<AddIcon />
</div>
</div>
</div>
);
})}
</div>
</>
);
};
export default Widget2InputCard3D;

View File

@ -0,0 +1,158 @@
import React, { useEffect, useState } from "react";
import MultiLevelDropdown from "../../../../ui/inputs/MultiLevelDropDown";
import { AddIcon } from "../../../../icons/ExportCommonIcons";
import RegularDropDown from "../../../../ui/inputs/RegularDropDown";
import useChartStore from "../../../../../store/useChartStore";
import { useSelectedZoneStore } from "../../../../../store/useZoneStore";
import { useWidgetStore } from "../../../../../store/useWidgetStore";
import axios from "axios";
import RenameInput from "../../../../ui/inputs/RenameInput";
const Widget3InputCard3D = () => {
const { selectedChartId } = useWidgetStore();
const [widgetName, setWidgetName] = useState("untited");
const { setMeasurements, updateDuration, updateName } = useChartStore();
const [duration, setDuration] = useState('1h')
const [dropDowndata, setDropDownData] = useState({});
const [selections, setSelections] = useState<Record<string, { name: string; fields: string }>>({});
const { selectedZone } = useSelectedZoneStore();
const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL;
const email = localStorage.getItem("email") || "";
const organization = email?.split("@")[1]?.split(".")[0]
console.log(selectedChartId);
useEffect(() => {
const fetchZoneData = async () => {
try {
const response = await axios.get(`http://${iotApiUrl}/getinput`);
if (response.status === 200) {
// console.log("dropdown data:", response.data);
setDropDownData(response.data);
} else {
console.log("Unexpected response:", response);
}
} catch (error) {
console.error("There was an error!", error);
}
};
fetchZoneData();
}, []);
useEffect(() => {
const fetchSavedInputes = async () => {
if (selectedChartId.id !== "") {
try {
const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/widget3D/${selectedChartId.id}/${organization}`);
if (response.status === 200) {
setSelections(response.data.Data.measurements)
setDuration(response.data.Data.duration)
setWidgetName(response.data.widgetName)
} else {
console.log("Unexpected response:", response);
}
} catch (error) {
console.error("There was an error!", error);
}
}
}
fetchSavedInputes();
}, [selectedChartId.id]);
useEffect(() => {
setMeasurements(selections);
updateDuration(duration);
updateName(widgetName);
}, [selections, duration, widgetName]);
const sendInputes = async (inputMeasurement: any, inputDuration: any, inputName: any) => {
try {
const response = await axios.post(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/3dwidget/save`, {
organization: organization,
zoneId: selectedZone.zoneId,
widget: {
id: selectedChartId.id,
widgetName: inputName,
Data: {
measurements: inputMeasurement,
duration: inputDuration
}
}
} as any);
if (response.status === 200) {
return true
} else {
console.log("Unexpected response:", response);
return false
}
} catch (error) {
console.error("There was an error!", error);
return false
}
}
const handleSelect = async (inputKey: string, selectedData: { name: string; fields: string } | null) => {
// async() => {
const newSelections = { ...selections };
if (selectedData === null) {
delete newSelections[inputKey];
} else {
newSelections[inputKey] = selectedData;
}
// setMeasurements(newSelections); // Update Zustand store
console.log(newSelections);
if (await sendInputes(newSelections, duration, widgetName)) {
setSelections(newSelections);
}
};
const handleSelectDuration = async (option: string) => {
if (await sendInputes(selections, option, widgetName)) {
setDuration(option);
}
};
const handleNameChange = async (name:any) => {
console.log('name change requested',name);
if (await sendInputes(selections, duration, name)) {
setWidgetName(name);
}
}
return (
<>
<div className="inputs-wrapper">
<div className="datas">
<div className="datas__label">Title</div>
<RenameInput value={widgetName} onRename={handleNameChange}/>
</div>
{[...Array(7)].map((_, index) => {
const inputKey = `input${index + 1}`;
return (
<div key={index} className="datas">
<div className="datas__label">Input {index + 1}</div>
<div className="datas__class">
<MultiLevelDropdown
data={dropDowndata}
onSelect={(selectedData) => handleSelect(inputKey, selectedData)}
onUnselect={() => handleSelect(inputKey, null)}
selectedValue={selections[inputKey]} // Load from Zustand
/>
<div className="icon">
<AddIcon />
</div>
</div>
</div>
);
})}
</div>
</>
);
};
export default Widget3InputCard3D;

View File

@ -0,0 +1,178 @@
import React, { useEffect, useState } from "react";
import MultiLevelDropdown from "../../../../ui/inputs/MultiLevelDropDown";
import { AddIcon } from "../../../../icons/ExportCommonIcons";
import RegularDropDown from "../../../../ui/inputs/RegularDropDown";
import useChartStore from "../../../../../store/useChartStore";
import { useSelectedZoneStore } from "../../../../../store/useZoneStore";
import { useWidgetStore } from "../../../../../store/useWidgetStore";
import axios from "axios";
import RenameInput from "../../../../ui/inputs/RenameInput";
type Props = {};
const Widget4InputCard3D = (props: Props) => {
const { selectedChartId } = useWidgetStore();
const [widgetName, setWidgetName] = useState("untited");
const { setMeasurements, updateDuration, updateName } = useChartStore();
const [duration, setDuration] = useState('1h')
const [dropDowndata, setDropDownData] = useState({});
const [selections, setSelections] = useState<Record<string, { name: string; fields: string }>>({});
const { selectedZone } = useSelectedZoneStore();
const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL;
const email = localStorage.getItem("email") || "";
const organization = email?.split("@")[1]?.split(".")[0]
console.log(selectedChartId);
useEffect(() => {
const fetchZoneData = async () => {
try {
const response = await axios.get(`http://${iotApiUrl}/getinput`);
if (response.status === 200) {
// console.log("dropdown data:", response.data);
setDropDownData(response.data);
} else {
console.log("Unexpected response:", response);
}
} catch (error) {
console.error("There was an error!", error);
}
};
fetchZoneData();
}, []);
useEffect(() => {
const fetchSavedInputes = async () => {
if (selectedChartId.id !== "") {
try {
const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/widget3D/${selectedChartId.id}/${organization}`);
if (response.status === 200) {
setSelections(response.data.Data.measurements)
setDuration(response.data.Data.duration)
setWidgetName(response.data.widgetName)
} else {
console.log("Unexpected response:", response);
}
} catch (error) {
console.error("There was an error!", error);
}
}
}
fetchSavedInputes();
}, [selectedChartId.id]);
// Sync Zustand state when component mounts
useEffect(() => {
setMeasurements(selections);
updateDuration(duration);
updateName(widgetName);
}, [selections, duration, widgetName]);
const sendInputes = async (inputMeasurement: any, inputDuration: any, inputName: any) => {
try {
const response = await axios.post(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/3dwidget/save`, {
organization: organization,
zoneId: selectedZone.zoneId,
widget: {
id: selectedChartId.id,
widgetName: inputName,
Data: {
measurements: inputMeasurement,
duration: inputDuration
}
}
} as any);
if (response.status === 200) {
return true
} else {
console.log("Unexpected response:", response);
return false
}
} catch (error) {
console.error("There was an error!", error);
return false
}
}
const handleSelect = async (inputKey: string, selectedData: { name: string; fields: string } | null) => {
// async() => {
const newSelections = { ...selections };
if (selectedData === null) {
delete newSelections[inputKey];
} else {
newSelections[inputKey] = selectedData;
}
// setMeasurements(newSelections); // Update Zustand store
console.log(newSelections);
if (await sendInputes(newSelections, duration, widgetName)) {
setSelections(newSelections);
}
// sendInputes(newSelections, duration); // Send data to server
// return newSelections;
// };
};
const handleSelectDuration = async (option: string) => {
if (await sendInputes(selections, option, widgetName)) {
setDuration(option);
}
// setDuration(option);
};
const handleNameChange = async (name:any) => {
console.log('name change requested',name);
if (await sendInputes(selections, duration, name)) {
setWidgetName(name);
}
}
return (
<>
<div className="inputs-wrapper">
<div className="datas">
<div className="datas__label">Title</div>
<RenameInput value={widgetName} onRename={handleNameChange}/>
</div>
{[...Array(1)].map((_, index) => {
const inputKey = `input${index + 1}`;
return (
<div key={index} className="datas">
<div className="datas__label">Input {index + 1}</div>
<div className="datas__class">
<MultiLevelDropdown
data={dropDowndata}
onSelect={(selectedData) => handleSelect(inputKey, selectedData)}
onUnselect={() => handleSelect(inputKey, null)}
selectedValue={selections[inputKey]} // Load from Zustand
/>
<div className="icon">
<AddIcon />
</div>
</div>
</div>
);
})}
</div>
<div>
<div className="datas">
<div className="datas__label">Duration</div>
<div className="datas__class">
<RegularDropDown
header={duration}
options={["1h", "2h", "12h"]}
onSelect={handleSelectDuration}
search={false}
/>
</div>
</div>
</div>
</>
);
};
export default Widget4InputCard3D;

View File

@ -4,6 +4,7 @@ import { AddIcon, RemoveIcon } from "../../../../icons/ExportCommonIcons";
import MultiLevelDropDown from "../../../../ui/inputs/MultiLevelDropDown";
import LineGrapInput from "../IotInputCards/LineGrapInput";
import RenameInput from "../../../../ui/inputs/RenameInput";
import InputSelecterComponent from "../IotInputCards/InputSelecterComponent";
// Define the data structure for demonstration purposes
const DATA_STRUCTURE = {
@ -118,7 +119,7 @@ const Data = () => {
};
});
};
// console.log("selectedChartId", selectedChartId);
return (
<div className="dataSideBar">
@ -133,8 +134,7 @@ const Data = () => {
{
chartDataGroups[selectedChartId?.id] &&
<>
<div className="sideBarHeader">2D Widget Input</div>
<LineGrapInput />
<InputSelecterComponent />
</>
}

View File

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

View File

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

View File

@ -1,4 +1,4 @@
import React from "react";
import React, { useEffect } from "react";
import {
CleanPannel,
EyeIcon,
@ -6,6 +6,8 @@ import {
} from "../../icons/RealTimeVisulationIcons";
import { panelData } from "../../../services/realTimeVisulization/zoneData/panel";
import { AddIcon } from "../../icons/ExportCommonIcons";
import { deletePanelApi } from "../../../services/realTimeVisulization/zoneData/deletePanel";
import { useSocketStore } from "../../../store/store";
// Define the type for `Side`
type Side = "top" | "bottom" | "left" | "right";
@ -16,7 +18,6 @@ interface ButtonsProps {
zoneName: string;
activeSides: Side[];
panelOrder: Side[];
lockedPanels: Side[];
zoneId: string;
zoneViewPortTarget: number[];
@ -34,7 +35,7 @@ interface ButtonsProps {
zoneName: string;
activeSides: Side[];
panelOrder: Side[];
lockedPanels: Side[];
zoneId: string;
zoneViewPortTarget: number[];
@ -58,6 +59,9 @@ const AddButtons: React.FC<ButtonsProps> = ({
setHiddenPanels,
hiddenPanels,
}) => {
const { visualizationSocket } = useSocketStore();
// Local state to track hidden panels
// Function to toggle lock/unlock a panel
@ -103,9 +107,13 @@ const AddButtons: React.FC<ButtonsProps> = ({
};
// Function to handle "+" button click
const handlePlusButtonClick = (side: Side) => {
const handlePlusButtonClick = async (side: 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(
(widget) => widget.panel !== side
);
@ -118,44 +126,68 @@ const AddButtons: React.FC<ButtonsProps> = ({
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);
// 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 {
const updatePanelData = async () => {
try {
// Get email and organization safely
const email = localStorage.getItem("email") || "";
const organization = email?.split("@")[1]?.split(".")[0] || "defaultOrg"; // Fallback value
// Panel does not exist: Create panel
try {
// Get email and organization safely with a default fallback
const email = localStorage.getItem("email") || "";
const organization = email?.split("@")[1]?.split(".")[0];
// Prevent duplicate side entries
const newActiveSides = selectedZone.activeSides.includes(side)
? [...selectedZone.activeSides]
: [...selectedZone.activeSides, side];
// Prevent duplicate side entries
const newActiveSides = selectedZone.activeSides.includes(side)
? [...selectedZone.activeSides]
: [...selectedZone.activeSides, side];
const updatedZone = {
...selectedZone,
activeSides: newActiveSides,
panelOrder: newActiveSides,
};
// API call
const response = await panelData(organization, selectedZone.zoneId, newActiveSides);
// Update state
setSelectedZone(updatedZone);
} catch (error) {
const updatedZone = {
...selectedZone,
activeSides: newActiveSides,
panelOrder: newActiveSides,
};
let addPanel = {
organization: organization,
zoneId: selectedZone.zoneId,
panelOrder: newActiveSides
}
};
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 (
<>
<div>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,69 +1,3 @@
// export function determinePosition(
// canvasRect: DOMRect,
// relativeX: number,
// relativeY: number
// ): {
// top: number | "auto";
// left: number | "auto";
// right: number | "auto";
// bottom: number | "auto";
// } {
// // Calculate the midpoints of the canvas
// const centerX = canvasRect.width / 2;
// const centerY = canvasRect.height / 2;
// // Initialize position with default values
// let position: {
// top: number | "auto";
// left: number | "auto";
// right: number | "auto";
// bottom: number | "auto";
// };
// if (relativeY < centerY) {
// // Top half
// if (relativeX < centerX) {
// // Left side
// position = {
// top: relativeY,
// left: relativeX,
// right: "auto",
// bottom: "auto",
// };
// } else {
// // Right side
// position = {
// top: relativeY,
// right: canvasRect.width - relativeX,
// left: "auto",
// bottom: "auto",
// };
// }
// } else {
// // Bottom half
// if (relativeX < centerX) {
// // Left side
// position = {
// bottom: canvasRect.height - relativeY,
// left: relativeX,
// right: "auto",
// top: "auto",
// };
// } else {
// // Right side
// position = {
// bottom: canvasRect.height - relativeY,
// right: canvasRect.width - relativeX,
// left: "auto",
// top: "auto",
// };
// }
// }
// return position;
// }
export function determinePosition(
canvasRect: DOMRect,
@ -87,36 +21,36 @@ export function determinePosition(
if (relativeY < centerY) {
if (relativeX < centerX) {
console.log("Top-left");
position = {
top: relativeY,
left: relativeX,
top: relativeY - 41.5,
left: relativeX - 125,
right: "auto",
bottom: "auto",
};
} else {
console.log("Top-right");
position = {
top: relativeY,
right: canvasRect.width - relativeX,
top: relativeY - 41.5,
right: canvasRect.width - relativeX - 125,
left: "auto",
bottom: "auto",
};
}
} else {
if (relativeX < centerX) {
console.log("Bottom-left");
position = {
bottom: canvasRect.height - relativeY,
left: relativeX,
bottom: canvasRect.height - relativeY - 41.5,
left: relativeX - 125,
right: "auto",
top: "auto",
};
} else {
console.log("Bottom-right");
position = {
bottom: canvasRect.height - relativeY,
right: canvasRect.width - relativeX,
bottom: canvasRect.height - relativeY - 41.5,
right: canvasRect.width - relativeX - 125,
left: "auto",
top: "auto",
};
@ -124,4 +58,4 @@ export function determinePosition(
}
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
disabled?: boolean;
value?: number;
onPointerUp?: (value: number) => void;
}
const InputRange: React.FC<InputToggleProps> = ({
@ -17,9 +18,10 @@ const InputRange: React.FC<InputToggleProps> = ({
min,
max,
disabled,
value = 5,
value,
onPointerUp,
}) => {
const [rangeValue, setRangeValue] = useState<number>(value);
const [rangeValue, setRangeValue] = useState<number>(value ? value : 5);
function handleChange(e: React.ChangeEvent<HTMLInputElement>) {
const newValue = parseInt(e.target.value); // Parse the value to an integer
@ -31,8 +33,22 @@ const InputRange: React.FC<InputToggleProps> = ({
}
}
useEffect(() => {
setRangeValue(value);
value && setRangeValue(value);
}, [value]);
function handlePointerUp(e: React.PointerEvent<HTMLInputElement>) {
const newValue = parseInt(e.currentTarget.value, 10); // Parse value correctly
if (onPointerUp) {
onPointerUp(newValue); // Call the callback function if it exists
}
}
function handlekey(e: React.KeyboardEvent<HTMLInputElement>) {
const newValue = parseInt(e.currentTarget.value, 10); // Parse value correctly
if (onPointerUp) {
onPointerUp(newValue); // Call the callback function if it exists
}
}
return (
<div className="input-range-container">
@ -52,6 +68,7 @@ const InputRange: React.FC<InputToggleProps> = ({
onChange={handleChange}
disabled={disabled}
value={rangeValue}
onPointerUp={handlePointerUp}
/>
<input
type="number"
@ -61,6 +78,12 @@ const InputRange: React.FC<InputToggleProps> = ({
value={rangeValue}
onChange={handleChange}
disabled={disabled}
onKeyUp={(e) => {
if (e.key === "ArrowUp" || e.key === "ArrowDown") {
console.log("e.key: ", e.key);
handlekey(e);
}
}}
/>
</div>
</div>

View File

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

View File

@ -0,0 +1,105 @@
import { StockIncreseIcon } from "../../../icons/RealTimeVisulationIcons";
import React, { useEffect, useMemo, useState } from "react";
import { Line } from "react-chartjs-2";
import io from "socket.io-client";
import useChartStore from "../../../../store/useChartStore";
import { useWidgetStore } from "../../../../store/useWidgetStore";
import axios from "axios";
const ProgressCard1 = ({ id,title,}: {
id: string,
title: string;
}) => {
const { measurements: chartMeasurements, duration: chartDuration, name: widgetName } = useChartStore();
const [measurements, setmeasurements] = useState<any>({});
const [duration, setDuration] = useState("1h")
const [name, setName] = useState(title)
const [value, setValue] = useState<any>('')
const { selectedChartId } = useWidgetStore();
const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL;
const email = localStorage.getItem("email") || "";
const organization = email?.split("@")[1]?.split(".")[0]
useEffect(() => {
const socket = io(`http://${iotApiUrl}`);
if (!iotApiUrl || !measurements || Object.keys(measurements).length === 0) return;
const inputData = {
measurements,
duration,
interval: 1000,
};
const startStream = () => {
socket.emit("lastInput", inputData);
};
socket.on("connect", startStream);
socket.on("lastOutput", (response) => {
const responseData = response.input1;
setValue(responseData);
});
return () => {
socket.off("lastOutput");
socket.emit("stop_stream"); // Stop streaming when component unmounts
socket.disconnect();
};
}, [measurements, duration]);
const fetchSavedInputes = async() => {
if (id !== "") {
try {
const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/WidgetData/${id}/${organization}`);
if (response.status === 200) {
setmeasurements(response.data.Data.measurements)
setDuration(response.data.Data.duration)
setName(response.data.widgetName)
} else {
console.log("Unexpected response:", response);
}
} catch (error) {
console.error("There was an error!", error);
}
}
}
useEffect(() => {
fetchSavedInputes();
}, []);
useEffect(() => {
if (selectedChartId?.id === id) {
fetchSavedInputes();
}
}
,[chartMeasurements, chartDuration, widgetName])
return(
<div className="chart progressBar">
<div className="header">{name}</div>
<div className="stock">
<span className="stock-item">
<span className="stockValues">
<div className="value">{value}</div>
<div className="key">Units</div>
</span>
<div className="stock-description">{
measurements ? `${measurements?.input1?.fields}` : 'description'}</div>
</span>
<div className="icon">
<StockIncreseIcon />
</div>
</div>
</div>
)
};
export default ProgressCard1;

View File

@ -0,0 +1,125 @@
import { StockIncreseIcon } from "../../../icons/RealTimeVisulationIcons";
import React, { useEffect, useMemo, useState } from "react";
import { Line } from "react-chartjs-2";
import io from "socket.io-client";
import useChartStore from "../../../../store/useChartStore";
import { useWidgetStore } from "../../../../store/useWidgetStore";
import axios from "axios";
const ProgressCard2 = ({ id,title,}: {
id: string,
title: string;
}) => {
const { measurements: chartMeasurements, duration: chartDuration, name: widgetName } = useChartStore();
const [measurements, setmeasurements] = useState<any>({});
const [duration, setDuration] = useState("1h")
const [name, setName] = useState(title)
const [value1, setValue1] = useState<any>('')
const [value2, setValue2] = useState<any>('')
const { selectedChartId } = useWidgetStore();
const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL;
const email = localStorage.getItem("email") || "";
const organization = email?.split("@")[1]?.split(".")[0]
useEffect(() => {
if (!iotApiUrl || !measurements || Object.keys(measurements).length === 0) return;
const socket = io(`http://${iotApiUrl}`);
const inputData = {
measurements,
duration,
interval: 1000,
};
const startStream = () => {
socket.emit("lastInput", inputData);
};
socket.on("connect", startStream);
socket.on("lastOutput", (response) => {
const responseData1 = response.input1;
const responseData2 = response.input2;
setValue1(responseData1);
setValue2(responseData2);
});
return () => {
socket.off("lastOutput");
socket.emit("stop_stream"); // Stop streaming when component unmounts
socket.disconnect();
};
}, [measurements, duration]);
const fetchSavedInputes = async() => {
if (id !== "") {
try {
const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/WidgetData/${id}/${organization}`);
if (response.status === 200) {
setmeasurements(response.data.Data.measurements)
setDuration(response.data.Data.duration)
setName(response.data.widgetName)
} else {
console.log("Unexpected response:", response);
}
} catch (error) {
console.error("There was an error!", error);
}
}
}
useEffect(() => {
fetchSavedInputes();
}, []);
useEffect(() => {
if (selectedChartId?.id === id) {
fetchSavedInputes();
}
}
,[chartMeasurements, chartDuration, widgetName])
return(
<div className="chart progressBar">
<div className="header">{name}</div>
<div className="stock">
<span className="stock-item">
<span className="stockValues">
<div className="value">{value1}</div>
<div className="key">Units</div>
</span>
<div className="stock-description">{
measurements ? `${measurements?.input1?.fields}` : 'description'}</div>
</span>
<div className="icon">
<StockIncreseIcon />
</div>
</div>
<div className="stock">
<span className="stock-item">
<span className="stockValues">
<div className="value">{value2}</div>
<div className="key">Units</div>
</span>
<div className="stock-description">{
measurements ? `${measurements?.input2?.fields}` : 'description'}</div>
</span>
<div className="icon">
<StockIncreseIcon />
</div>
</div>
</div>
)
};
export default ProgressCard2;

View File

@ -1,8 +1,8 @@
const FleetEfficiency = () => {
const progress = 75; // Example progress value (0-100)
const progress = 50; // Example progress value (0-100)
// Calculate the rotation angle for the progress bar
const rotationAngle = -90 + progress * 3.6; // Progress starts from the left (-90°)
const rotationAngle = 45 + progress * 1.8;
const handleDragStart = (event: React.DragEvent<HTMLDivElement>) => {
const rect = event.currentTarget.getBoundingClientRect(); // Get position

View File

@ -1,19 +1,99 @@
import React from 'react'
import React, { useState, useEffect } from 'react'
import { Line } from 'react-chartjs-2'
import useChartStore from '../../../../store/useChartStore';
import { useWidgetStore } from '../../../../store/useWidgetStore';
import axios from 'axios';
import io from "socket.io-client";
type Props = {}
const FleetEfficiencyComponent = ({object}: any) => {
const [ progress, setProgress ] = useState<any>(0)
const [measurements, setmeasurements] = useState<any>({});
const [duration, setDuration] = useState("1h")
const [name, setName] = useState(object.header ? object.header : '')
const email = localStorage.getItem("email") || "";
const organization = email?.split("@")[1]?.split(".")[0]
const { header, flotingDuration, flotingMeasurements } = useChartStore();
const { selectedChartId } = useWidgetStore();
const FleetEfficiencyComponent = ({
object
}: any) => {
const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL;
// Calculate the rotation angle for the progress bar
const rotationAngle = 45 + progress * 1.8;
useEffect(() => {
if (!iotApiUrl || !measurements || Object.keys(measurements).length === 0) return;
const socket = io(`http://${iotApiUrl}`);
const inputData = {
measurements,
duration,
interval: 1000,
};
const startStream = () => {
socket.emit("lastInput", inputData);
};
socket.on("connect", startStream);
socket.on("lastOutput", (response) => {
const responseData = response.input1;
console.log(responseData);
if (typeof responseData === "number") {
console.log("It's a number!");
setProgress(responseData);
}
});
return () => {
socket.off("lastOutput");
socket.emit("stop_stream"); // Stop streaming when component unmounts
socket.disconnect();
};
}, [measurements, duration, iotApiUrl]);
const fetchSavedInputes = async() => {
if (object?.id !== "") {
try {
const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/A_floatWidget/${object?.id}/${organization}`);
if (response.status === 200) {
setmeasurements(response.data.Data.measurements)
setDuration(response.data.Data.duration)
setName(response.data.header)
} else {
console.log("Unexpected response:", response);
}
} catch (error) {
console.error("There was an error!", error);
}
}
}
useEffect(() => {
fetchSavedInputes();
}, []);
useEffect(() => {
if (selectedChartId?.id === object?.id) {
fetchSavedInputes();
}
}
,[header, flotingDuration, flotingMeasurements])
return (
<>
<h2 className="header">Fleet Efficiency</h2>
<h2 className="header">{name}</h2>
<div className="progressContainer">
<div className="progress">
<div className="barOverflow">
<div
className="bar"
style={{ transform: `rotate(${object.value}deg)` }}
style={{ transform: `rotate(${rotationAngle}deg)` }}
></div>
</div>
</div>
@ -21,7 +101,7 @@ const FleetEfficiencyComponent = ({
<div className="scaleLabels">
<span>0%</span>
<div className="centerText">
<div className="percentage">{object.per}%</div>
<div className="percentage">{progress}%</div>
<div className="status">Optimal</div>
</div>
<span>100%</span>

View File

@ -1,21 +1,95 @@
import React from 'react'
import { WalletIcon } from '../../../icons/3dChartIcons'
import React, { useState, useEffect } from 'react'
import { Line } from 'react-chartjs-2'
import useChartStore from '../../../../store/useChartStore';
import { useWidgetStore } from '../../../../store/useWidgetStore';
import axios from 'axios';
import io from "socket.io-client";
import { WalletIcon } from '../../../icons/3dChartIcons';
const TotalCardComponent = ({
object
}: any) => {
const { setSelectedChartId } =
useWidgetStore();
const [ progress, setProgress ] = useState<any>(0)
const [measurements, setmeasurements] = useState<any>({});
const [duration, setDuration] = useState("1h")
const [name, setName] = useState(object.header ? object.header : '')
const email = localStorage.getItem("email") || "";
const organization = email?.split("@")[1]?.split(".")[0]
const { header, flotingDuration, flotingMeasurements } = useChartStore();
const { selectedChartId } = useWidgetStore();
const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL;
useEffect(() => {
if (!iotApiUrl || !measurements || Object.keys(measurements).length === 0) return;
const socket = io(`http://${iotApiUrl}`);
const inputData = {
measurements,
duration,
interval: 1000,
};
const startStream = () => {
socket.emit("lastInput", inputData);
};
socket.on("connect", startStream);
socket.on("lastOutput", (response) => {
const responseData = response.input1;
if (typeof responseData === "number") {
setProgress(responseData);
}
});
return () => {
socket.off("lastOutput");
socket.emit("stop_stream"); // Stop streaming when component unmounts
socket.disconnect();
};
}, [measurements, duration, iotApiUrl]);
const fetchSavedInputes = async() => {
if (object?.id !== "") {
try {
const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/A_floatWidget/${object?.id}/${organization}`);
if (response.status === 200) {
setmeasurements(response.data.Data.measurements)
setDuration(response.data.Data.duration)
setName(response.data.header)
} else {
console.log("Unexpected response:", response);
}
} catch (error) {
console.error("There was an error!", error);
}
}
}
useEffect(() => {
fetchSavedInputes();
}, []);
useEffect(() => {
if (selectedChartId?.id === object?.id) {
fetchSavedInputes();
}
}
,[header, flotingDuration, flotingMeasurements])
return (
<>
<div className="header-wrapper" onClick={() => {
setSelectedChartId(object.id)
}}>
<div className="header">{object.header}</div>
<div className="header-wrapper" >
<div className="header">{name}</div>
<div className="data-values">
<div className="value">{object.value}</div>
<div className="value">{progress}</div>
<div className="per">{object.per}</div>
</div>
</div>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,32 +1,43 @@
import { setEnvironment } from './setEnvironment';
import { setEnvironment } from "./setEnvironment";
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`;
export const findEnvironment = async (organization: string, userId: string) => {
try {
const response = await fetch(`${url_Backend_dwinzo}/api/v1/findEnvironments/${organization}/${userId}`, {
method: "GET",
headers: {
"Content-Type": "application/json",
},
});
try {
const response = await fetch(
`${url_Backend_dwinzo}/api/v1/findEnvironments/${organization}/${userId}`,
{
method: "GET",
headers: {
"Content-Type": "application/json",
},
}
);
if (!response.ok) {
throw new Error("Failed to get wall and roof visibility");
}
const result = await response.json();
if (result === "user not found") {
const userpos = setEnvironment(organization, userId, false, false, false);
return userpos;
} else {
return result;
}
} catch (error) {
if (error instanceof Error) {
throw new Error(error.message);
} else {
throw new Error("An unknown error occurred");
}
if (!response.ok) {
throw new Error("Failed to get wall and roof visibility");
}
};
const result = await response.json();
if (result === "user not found") {
const userpos = setEnvironment(
organization,
userId,
false,
false,
false,
0,
true
);
return userpos;
} else {
return result;
}
} catch (error) {
if (error instanceof Error) {
throw new Error(error.message);
} else {
throw new Error("An unknown error occurred");
}
}
};

View File

@ -1,26 +1,45 @@
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`;
export const setEnvironment = async (organization: string, userId: string, wallVisibility: Boolean, roofVisibility: Boolean, shadowVisibility: Boolean) => {
try {
const response = await fetch(`${url_Backend_dwinzo}/api/v1/setEvironments`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ organization, userId, wallVisibility, roofVisibility, shadowVisibility }),
});
export const setEnvironment = async (
organization: string,
userId: string,
wallVisibility: Boolean,
roofVisibility: Boolean,
shadowVisibility: Boolean,
renderDistance: number,
limitDistance: boolean
) => {
try {
const response = await fetch(
`${url_Backend_dwinzo}/api/v1/setEvironments`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
organization,
userId,
wallVisibility,
roofVisibility,
shadowVisibility,
renderDistance,
limitDistance,
}),
}
);
if (!response.ok) {
throw new Error("Failed to set wall and roof visibility");
}
const result = await response.json();
return result;
} catch (error) {
if (error instanceof Error) {
throw new Error(error.message);
} else {
throw new Error("An unknown error occurred");
}
if (!response.ok) {
throw new Error("Failed to set wall and roof visibility");
}
};
const result = await response.json();
return result;
} catch (error) {
if (error instanceof Error) {
throw new Error(error.message);
} else {
throw new Error("An unknown error occurred");
}
}
};

View File

@ -6,8 +6,8 @@ export const fetchAssets = async () => {
throw new Error("Network response was not ok");
}
const result = await response.json();
const last10Assets = result.slice(-10);
console.log('last10Assets: ', last10Assets);
// const last10Assets = result.slice(-10);
// console.log('last10Assets: ', last10Assets);
return result;
} catch (error) {
console.log("error: ", error);

View File

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

View File

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

View File

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

View File

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

View File

@ -11,16 +11,28 @@ export const useSocketStore = create<any>((set: any, get: any) => ({
return;
}
const socket = io(`http://${process.env.REACT_APP_SERVER_SOCKET_API_BASE_URL}/Builder`, {
reconnection: false,
auth: { email, organization },
});
const socket = io(
`http://${process.env.REACT_APP_SERVER_SOCKET_API_BASE_URL}/Builder`,
{
reconnection: false,
auth: { email, organization },
}
);
set({ socket });
const visualizationSocket = io(
`http://${process.env.REACT_APP_SERVER_SOCKET_API_BASE_URL}/Visualization`,
{
reconnection: false,
auth: { email, organization },
}
);
set({ socket, visualizationSocket });
},
disconnectSocket: () => {
set((state: any) => {
state.socket?.disconnect();
state.visualizationSocket?.disconnect();
return { socket: null };
});
},
@ -205,7 +217,9 @@ export const useActiveLayer = create<any>((set: any) => ({
interface RefTextUpdateState {
refTextupdate: number;
setRefTextUpdate: (callback: (currentValue: number) => number | number) => void;
setRefTextUpdate: (
callback: (currentValue: number) => number | number
) => void;
}
export const useRefTextUpdate = create<RefTextUpdateState>((set) => ({
@ -213,7 +227,9 @@ export const useRefTextUpdate = create<RefTextUpdateState>((set) => ({
setRefTextUpdate: (callback) =>
set((state) => ({
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) => ({
renderDistance: 50,
renderDistance: 40,
setRenderDistance: (x: any) => set({ renderDistance: x }),
}));
@ -393,4 +409,22 @@ export const useWidgetSubOption = create<any>((set: any) => ({
widgetSubOption: "2D",
setWidgetSubOption: (x: any) => set({ widgetSubOption: x }),
}));
export const useLimitDistance = create<any>((set: any) => ({
limitDistance: true,
setLimitDistance: (x: any) => set({ limitDistance: x }),
}));
export const useTileDistance = create<any>((set: any) => ({
gridValue: { size: 300, divisions: 75 },
planeValue: { height: 300, width: 300 },
setGridValue: (value: any) =>
set((state: any) => ({
gridValue: { ...state.gridValue, ...value },
})),
setPlaneValue: (value: any) =>
set((state: any) => ({
planeValue: { ...state.planeValue, ...value },
})),
}));

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -253,7 +253,7 @@
.user-profile-container {
display: flex;
.user-profile{
.user-profile {
background: var(--accent-color);
color: var(--primary-color);
}
@ -320,9 +320,7 @@
.dataSideBar {
.inputs-wrapper {
.datas {
.input-value {
padding: 5px 10px;
}
@ -688,7 +686,7 @@
font-weight: var(--font-weight-regular);
padding: 8px 0;
}
.input-toggle-container{
.input-toggle-container {
padding: 0;
margin-bottom: 6px;
}
@ -963,6 +961,7 @@
padding: 0 6px;
.assets-wrapper {
width: 100%;
position: relative;
margin: 8px 10px;
@ -1010,9 +1009,11 @@
top: 50%;
right: -10px;
transform: translate(0, -50%);
background: linear-gradient(144.19deg,
#f1e7cd 16.62%,
#fffaef 85.81%);
background: linear-gradient(
144.19deg,
#f1e7cd 16.62%,
#fffaef 85.81%
);
}
.category-image {
@ -1075,4 +1076,52 @@
cursor: pointer;
}
}
}
}
.assets-container {
width: 100%;
display: flex;
flex-direction: row;
flex-wrap: wrap;
height: 100%;
gap: 3px;
padding: 10px 0;
.assets {
width: 117px;
height: 95px;
border-radius: 3.59px;
background-color: var(--background-color-gray);
padding: 8px;
padding-top: 12px;
font-weight: $medium-weight;
position: relative;
overflow: hidden;
.asset-name {
position: relative;
z-index: 3;
font-size: var(--font-size-regular);
}
.asset-image {
height: 100%;
width: 100%;
position: absolute;
// top: 50%;
// right: 5px;
// transform: translate(0, -50%);
top: 0;
left: 0;
z-index: 2;
}
}
}
.assets-result {
width: 100%;
height: 100%;
margin: 8px 10px;
.assets-wrapper {
margin: 0;
}
}

View File

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

View File

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

View File

@ -59,39 +59,39 @@ export type ThreeDimension = {
};
export type GridConfig = {
size: number;
divisions: number;
primaryColor: string;
secondaryColor: string;
position2D: [x: number, y: number, z: number];
position3D: [x: number, y: number, z: number];
}
size: number;
divisions: number;
primaryColor: string;
secondaryColor: string;
position2D: [x: number, y: number, z: number];
position3D: [x: number, y: number, z: number];
};
export type PlaneConfig = {
position2D: [x: number, y: number, z: number];
position3D: [x: number, y: number, z: number];
rotation: number;
width: number;
height: number;
color: string;
}
position2D: [x: number, y: number, z: number];
position3D: [x: number, y: number, z: number];
rotation: number;
width: number;
height: number;
color: string;
};
export type ShadowConfig = {
shadowOffset: number,
shadowmapSizewidth: number,
shadowmapSizeheight: number,
shadowcamerafar: number,
shadowcameranear: number,
shadowcameratop: number,
shadowcamerabottom: number,
shadowcameraleft: number,
shadowcameraright: number,
shadowbias: number,
shadownormalBias: number,
shadowMaterialPosition: [x: number, y: number, z: number],
shadowMaterialRotation: [x: number, y: number, z: number],
shadowMaterialOpacity: number,
}
shadowOffset: number;
shadowmapSizewidth: number;
shadowmapSizeheight: number;
shadowcamerafar: number;
shadowcameranear: number;
shadowcameratop: number;
shadowcamerabottom: number;
shadowcameraleft: number;
shadowcameraright: number;
shadowbias: number;
shadownormalBias: number;
shadowMaterialPosition: [x: number, y: number, z: number];
shadowMaterialRotation: [x: number, y: number, z: number];
shadowMaterialOpacity: number;
};
export type SkyConfig = {
defaultTurbidity: number;
@ -109,34 +109,34 @@ export type AssetConfig = {
};
export type PointConfig = {
defaultInnerColor: string;
defaultOuterColor: string;
deleteColor: string;
boxScale: [number, number, number];
wallOuterColor: string;
floorOuterColor: string;
aisleOuterColor: string;
zoneOuterColor: string;
snappingThreshold: number;
}
defaultInnerColor: string;
defaultOuterColor: string;
deleteColor: string;
boxScale: [number, number, number];
wallOuterColor: string;
floorOuterColor: string;
aisleOuterColor: string;
zoneOuterColor: string;
snappingThreshold: number;
};
export type LineConfig = {
tubularSegments: number;
radius: number;
radialSegments: number;
wallName: string;
floorName: string;
aisleName: string;
zoneName: string;
referenceName: string;
lineIntersectionPoints: number;
defaultColor: string;
wallColor: string;
floorColor: string;
aisleColor: string;
zoneColor: string;
helperColor: string;
}
tubularSegments: number;
radius: number;
radialSegments: number;
wallName: string;
floorName: string;
aisleName: string;
zoneName: string;
referenceName: string;
lineIntersectionPoints: number;
defaultColor: string;
wallColor: string;
floorColor: string;
aisleColor: string;
zoneColor: string;
helperColor: string;
};
export type WallConfig = {
defaultColor: string;
@ -145,10 +145,10 @@ export type WallConfig = {
};
export type FloorConfig = {
defaultColor: string;
height: number;
textureScale: number;
}
defaultColor: string;
height: number;
textureScale: number;
};
export type RoofConfig = {
defaultColor: string;
@ -156,16 +156,16 @@ export type RoofConfig = {
};
export type AisleConfig = {
width: number;
height: number;
defaultColor: number;
}
width: number;
height: number;
defaultColor: number;
};
export type ZoneConfig = {
defaultColor: string;
height: number;
color: string;
}
defaultColor: string;
height: number;
color: string;
};
export type ColumnConfig = {
defaultColor: string;
@ -242,24 +242,24 @@ export const threeDimension: ThreeDimension = {
export const camPositionUpdateInterval: number = 200; // Interval for updating the camera position
export const gridConfig: GridConfig = {
size: 300, // Size of the grid
divisions: 75, // Number of divisions in the grid
primaryColor: savedTheme === "dark" ? "#131313" : "#d5d5d5", // Primary color of the grid
secondaryColor: savedTheme === "dark" ? "#434343" : "#e3e3e3", // Secondary color of the grid
size: 300, // Size of the grid
divisions: 75, // Number of divisions in the grid
primaryColor: savedTheme === "dark" ? "#131313" : "#d5d5d5", // Primary color of the grid
secondaryColor: savedTheme === "dark" ? "#434343" : "#e3e3e3", // Secondary color of the grid
position2D: [0, 0.1, 0], // Position of the grid in 2D view
position3D: [0, -0.5, 0], // Position of the grid in 3D view
}
position2D: [0, 0.1, 0], // Position of the grid in 2D view
position3D: [0, -0.5, 0], // Position of the grid in 3D view
};
export const planeConfig: PlaneConfig = {
position2D: [0, -0.5, 0], // Position of the plane
position3D: [0, -0.65, 0], // Position of the plane
rotation: -Math.PI / 2, // Rotation of the plane
width: 300, // Width of the plane
height: 300, // Height of the plane
color: savedTheme === "dark" ? "#323232" : "#f3f3f3" // Color of the plane
}
width: 300, // Width of the plane
height: 300, // Height of the plane
color: savedTheme === "dark" ? "#323232" : "#f3f3f3", // Color of the plane
};
export const shadowConfig: ShadowConfig = {
shadowOffset: 50, // Offset of the shadow
@ -349,10 +349,10 @@ export const aisleConfig: AisleConfig = {
};
export const zoneConfig: ZoneConfig = {
defaultColor: "black", // Default color of the zones
height: 3,
color: "#8656DF" // Color of the zones
}
defaultColor: "black", // Default color of the zones
height: 3,
color: "#8656DF", // Color of the zones
};
export const columnConfig: ColumnConfig = {
defaultColor: "White", // Default color of the columns

View File

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