Merge remote-tracking branch 'origin/ui' into simulation
This commit is contained in:
@@ -220,7 +220,7 @@ const ProductionCapacity: React.FC<ProductionCapacityProps> = ({
|
|||||||
// e.stopPropagation();
|
// e.stopPropagation();
|
||||||
}}
|
}}
|
||||||
wrapperClass="pointer-none"
|
wrapperClass="pointer-none"
|
||||||
className={`${selectedChartId?.id === id ? "activeChart" : ""}`}
|
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className={`productionCapacity-wrapper card ${selectedChartId?.id === id ? "activeChart" : ""}`}
|
className={`productionCapacity-wrapper card ${selectedChartId?.id === id ? "activeChart" : ""}`}
|
||||||
|
|||||||
@@ -46,20 +46,32 @@ interface ReturnOfInvestmentProps {
|
|||||||
rotation: [number, number, number];
|
rotation: [number, number, number];
|
||||||
onContextMenu?: (event: React.MouseEvent) => void;
|
onContextMenu?: (event: React.MouseEvent) => void;
|
||||||
}
|
}
|
||||||
const ReturnOfInvestment: React.FC<ReturnOfInvestmentProps> = ({ id, type, position, rotation, onContextMenu }) => {
|
const ReturnOfInvestment: React.FC<ReturnOfInvestmentProps> = ({
|
||||||
|
id,
|
||||||
|
type,
|
||||||
|
position,
|
||||||
|
rotation,
|
||||||
|
onContextMenu,
|
||||||
|
}) => {
|
||||||
const { selectedChartId, setSelectedChartId } = useWidgetStore();
|
const { selectedChartId, setSelectedChartId } = useWidgetStore();
|
||||||
const { measurements: chartMeasurements, duration: chartDuration, name: widgetName } = useChartStore();
|
const {
|
||||||
|
measurements: chartMeasurements,
|
||||||
|
duration: chartDuration,
|
||||||
|
name: widgetName,
|
||||||
|
} = useChartStore();
|
||||||
const [measurements, setmeasurements] = useState<any>({});
|
const [measurements, setmeasurements] = useState<any>({});
|
||||||
const [duration, setDuration] = useState("1h")
|
const [duration, setDuration] = useState("1h");
|
||||||
const [name, setName] = useState("Widget")
|
const [name, setName] = useState("Widget");
|
||||||
const [chartData, setChartData] = useState<{ labels: string[]; datasets: any[] }>({
|
const [chartData, setChartData] = useState<{
|
||||||
|
labels: string[];
|
||||||
|
datasets: any[];
|
||||||
|
}>({
|
||||||
labels: [],
|
labels: [],
|
||||||
datasets: [],
|
datasets: [],
|
||||||
});
|
});
|
||||||
const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL;
|
const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL;
|
||||||
const email = localStorage.getItem("email") || "";
|
const email = localStorage.getItem("email") || "";
|
||||||
const organization = email?.split("@")[1]?.split(".")[0]
|
const organization = email?.split("@")[1]?.split(".")[0];
|
||||||
// Improved sample data for the smooth curve graph (single day)
|
// Improved sample data for the smooth curve graph (single day)
|
||||||
const graphData: ChartData<"line"> = {
|
const graphData: ChartData<"line"> = {
|
||||||
labels: [
|
labels: [
|
||||||
@@ -129,7 +141,8 @@ const ReturnOfInvestment: React.FC<ReturnOfInvestmentProps> = ({ id, type, posit
|
|||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!iotApiUrl || !measurements || Object.keys(measurements).length === 0) return;
|
if (!iotApiUrl || !measurements || Object.keys(measurements).length === 0)
|
||||||
|
return;
|
||||||
|
|
||||||
const socket = io(`http://${iotApiUrl}`);
|
const socket = io(`http://${iotApiUrl}`);
|
||||||
|
|
||||||
@@ -139,7 +152,6 @@ const ReturnOfInvestment: React.FC<ReturnOfInvestmentProps> = ({ id, type, posit
|
|||||||
interval: 1000,
|
interval: 1000,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const startStream = () => {
|
const startStream = () => {
|
||||||
socket.emit("lineInput", inputData);
|
socket.emit("lineInput", inputData);
|
||||||
};
|
};
|
||||||
@@ -157,8 +169,10 @@ const ReturnOfInvestment: React.FC<ReturnOfInvestmentProps> = ({ id, type, posit
|
|||||||
return {
|
return {
|
||||||
label: datasetKey,
|
label: datasetKey,
|
||||||
data: responseData[datasetKey]?.values ?? [],
|
data: responseData[datasetKey]?.values ?? [],
|
||||||
borderColor: index === 0 ? "rgba(75, 192, 192, 1)" : "rgba(255, 99, 132, 1)", // Light blue color
|
borderColor:
|
||||||
backgroundColor: index === 0 ? "rgba(75, 192, 192, 0.2)" : "rgba(255, 99, 132, 0.2)",
|
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,
|
fill: true,
|
||||||
tension: 0.4, // Smooth curve effect
|
tension: 0.4, // Smooth curve effect
|
||||||
pointRadius: 0, // Hide dots
|
pointRadius: 0, // Hide dots
|
||||||
@@ -177,14 +191,15 @@ const ReturnOfInvestment: React.FC<ReturnOfInvestmentProps> = ({ id, type, posit
|
|||||||
}, [measurements, duration, iotApiUrl]);
|
}, [measurements, duration, iotApiUrl]);
|
||||||
|
|
||||||
const fetchSavedInputes = async () => {
|
const fetchSavedInputes = async () => {
|
||||||
|
|
||||||
if (id !== "") {
|
if (id !== "") {
|
||||||
try {
|
try {
|
||||||
const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/widget3D/${id}/${organization}`);
|
const response = await axios.get(
|
||||||
|
`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/widget3D/${id}/${organization}`
|
||||||
|
);
|
||||||
if (response.status === 200) {
|
if (response.status === 200) {
|
||||||
setmeasurements(response.data.Data.measurements)
|
setmeasurements(response.data.Data.measurements);
|
||||||
setDuration(response.data.Data.duration)
|
setDuration(response.data.Data.duration);
|
||||||
setName(response.data.widgetName)
|
setName(response.data.widgetName);
|
||||||
} else {
|
} else {
|
||||||
console.log("Unexpected response:", response);
|
console.log("Unexpected response:", response);
|
||||||
}
|
}
|
||||||
@@ -192,7 +207,7 @@ const ReturnOfInvestment: React.FC<ReturnOfInvestmentProps> = ({ id, type, posit
|
|||||||
console.error("There was an error!", error);
|
console.error("There was an error!", error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchSavedInputes();
|
fetchSavedInputes();
|
||||||
@@ -202,8 +217,7 @@ const ReturnOfInvestment: React.FC<ReturnOfInvestmentProps> = ({ id, type, posit
|
|||||||
if (selectedChartId?.id === id) {
|
if (selectedChartId?.id === id) {
|
||||||
fetchSavedInputes();
|
fetchSavedInputes();
|
||||||
}
|
}
|
||||||
}
|
}, [chartMeasurements, chartDuration, widgetName]);
|
||||||
, [chartMeasurements, chartDuration, widgetName])
|
|
||||||
const rotationDegrees = {
|
const rotationDegrees = {
|
||||||
x: (rotation[0] * 180) / Math.PI,
|
x: (rotation[0] * 180) / Math.PI,
|
||||||
y: (rotation[1] * 180) / Math.PI,
|
y: (rotation[1] * 180) / Math.PI,
|
||||||
@@ -215,27 +229,32 @@ const ReturnOfInvestment: React.FC<ReturnOfInvestmentProps> = ({ id, type, posit
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Html position={[position[0], position[1], position[2]]}
|
<Html
|
||||||
|
position={[position[0], position[1], position[2]]}
|
||||||
scale={[0.5, 0.5, 0.5]}
|
scale={[0.5, 0.5, 0.5]}
|
||||||
transform
|
transform
|
||||||
zIndexRange={[1, 0]}
|
zIndexRange={[1, 0]}
|
||||||
sprite
|
sprite
|
||||||
style={{
|
style={{
|
||||||
transform: transformStyle.transform,
|
transform: transformStyle.transform,
|
||||||
transformStyle: 'preserve-3d',
|
transformStyle: "preserve-3d",
|
||||||
transition: 'transform 0.1s ease-out'
|
transition: "transform 0.1s ease-out",
|
||||||
|
|
||||||
}}
|
}}
|
||||||
className={`${selectedChartId?.id === id ? "activeChart" : ""}`}
|
|
||||||
>
|
>
|
||||||
<div className="returnOfInvestment card"
|
<div
|
||||||
|
className={`returnOfInvestment card ${
|
||||||
|
selectedChartId?.id === id ? "activeChart" : ""
|
||||||
|
}`}
|
||||||
onClick={() => setSelectedChartId({ id: id, type: type })}
|
onClick={() => setSelectedChartId({ id: id, type: type })}
|
||||||
onContextMenu={onContextMenu}
|
onContextMenu={onContextMenu}
|
||||||
>
|
>
|
||||||
<div className="header">Return of Investment</div>
|
<div className="header">Return of Investment</div>
|
||||||
<div className="lineGraph charts">
|
<div className="lineGraph charts">
|
||||||
{/* Smooth curve graph with two datasets */}
|
{/* Smooth curve graph with two datasets */}
|
||||||
<SmoothLineGraphComponent data={Object.keys(measurements).length > 0 ? chartData : graphData} options={graphOptions} />
|
<SmoothLineGraphComponent
|
||||||
|
data={Object.keys(measurements).length > 0 ? chartData : graphData}
|
||||||
|
options={graphOptions}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="returns-wrapper">
|
<div className="returns-wrapper">
|
||||||
<div className="icon">
|
<div className="icon">
|
||||||
|
|||||||
@@ -13,16 +13,26 @@ interface StateWorkingProps {
|
|||||||
rotation: [number, number, number];
|
rotation: [number, number, number];
|
||||||
onContextMenu?: (event: React.MouseEvent) => void;
|
onContextMenu?: (event: React.MouseEvent) => void;
|
||||||
}
|
}
|
||||||
const StateWorking: React.FC<StateWorkingProps> = ({ id, type, position, rotation, onContextMenu }) => {
|
const StateWorking: React.FC<StateWorkingProps> = ({
|
||||||
|
id,
|
||||||
|
type,
|
||||||
|
position,
|
||||||
|
rotation,
|
||||||
|
onContextMenu,
|
||||||
|
}) => {
|
||||||
const { selectedChartId, setSelectedChartId } = useWidgetStore();
|
const { selectedChartId, setSelectedChartId } = useWidgetStore();
|
||||||
const { measurements: chartMeasurements, duration: chartDuration, name: widgetName } = useChartStore();
|
const {
|
||||||
|
measurements: chartMeasurements,
|
||||||
|
duration: chartDuration,
|
||||||
|
name: widgetName,
|
||||||
|
} = useChartStore();
|
||||||
const [measurements, setmeasurements] = useState<any>({});
|
const [measurements, setmeasurements] = useState<any>({});
|
||||||
const [duration, setDuration] = useState("1h")
|
const [duration, setDuration] = useState("1h");
|
||||||
const [name, setName] = useState("Widget")
|
const [name, setName] = useState("Widget");
|
||||||
const [datas, setDatas] = useState<any>({});
|
const [datas, setDatas] = useState<any>({});
|
||||||
const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL;
|
const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL;
|
||||||
const email = localStorage.getItem("email") || "";
|
const email = localStorage.getItem("email") || "";
|
||||||
const organization = email?.split("@")[1]?.split(".")[0]
|
const organization = email?.split("@")[1]?.split(".")[0];
|
||||||
// const datas = [
|
// const datas = [
|
||||||
// { key: "Oil Tank:", value: "24/341" },
|
// { key: "Oil Tank:", value: "24/341" },
|
||||||
// { key: "Oil Refin:", value: 36.023 },
|
// { key: "Oil Refin:", value: 36.023 },
|
||||||
@@ -33,7 +43,8 @@ const StateWorking: React.FC<StateWorkingProps> = ({ id, type, position, rotatio
|
|||||||
// ];
|
// ];
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!iotApiUrl || !measurements || Object.keys(measurements).length === 0) return;
|
if (!iotApiUrl || !measurements || Object.keys(measurements).length === 0)
|
||||||
|
return;
|
||||||
const socket = io(`http://${iotApiUrl}`);
|
const socket = io(`http://${iotApiUrl}`);
|
||||||
const inputData = {
|
const inputData = {
|
||||||
measurements,
|
measurements,
|
||||||
@@ -47,7 +58,6 @@ const StateWorking: React.FC<StateWorkingProps> = ({ id, type, position, rotatio
|
|||||||
socket.on("lastOutput", (response) => {
|
socket.on("lastOutput", (response) => {
|
||||||
const responseData = response;
|
const responseData = response;
|
||||||
|
|
||||||
|
|
||||||
setDatas(responseData);
|
setDatas(responseData);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -59,14 +69,15 @@ const StateWorking: React.FC<StateWorkingProps> = ({ id, type, position, rotatio
|
|||||||
}, [measurements, duration, iotApiUrl]);
|
}, [measurements, duration, iotApiUrl]);
|
||||||
|
|
||||||
const fetchSavedInputes = async () => {
|
const fetchSavedInputes = async () => {
|
||||||
|
|
||||||
if (id !== "") {
|
if (id !== "") {
|
||||||
try {
|
try {
|
||||||
const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/widget3D/${id}/${organization}`);
|
const response = await axios.get(
|
||||||
|
`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/widget3D/${id}/${organization}`
|
||||||
|
);
|
||||||
if (response.status === 200) {
|
if (response.status === 200) {
|
||||||
setmeasurements(response.data.Data.measurements)
|
setmeasurements(response.data.Data.measurements);
|
||||||
setDuration(response.data.Data.duration)
|
setDuration(response.data.Data.duration);
|
||||||
setName(response.data.widgetName)
|
setName(response.data.widgetName);
|
||||||
} else {
|
} else {
|
||||||
console.log("Unexpected response:", response);
|
console.log("Unexpected response:", response);
|
||||||
}
|
}
|
||||||
@@ -74,10 +85,7 @@ const StateWorking: React.FC<StateWorkingProps> = ({ id, type, position, rotatio
|
|||||||
console.error("There was an error!", error);
|
console.error("There was an error!", error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchSavedInputes();
|
fetchSavedInputes();
|
||||||
@@ -87,8 +95,7 @@ const StateWorking: React.FC<StateWorkingProps> = ({ id, type, position, rotatio
|
|||||||
if (selectedChartId?.id === id) {
|
if (selectedChartId?.id === id) {
|
||||||
fetchSavedInputes();
|
fetchSavedInputes();
|
||||||
}
|
}
|
||||||
}
|
}, [chartMeasurements, chartDuration, widgetName]);
|
||||||
, [chartMeasurements, chartDuration, widgetName])
|
|
||||||
|
|
||||||
const rotationDegrees = {
|
const rotationDegrees = {
|
||||||
x: (rotation[0] * 180) / Math.PI,
|
x: (rotation[0] * 180) / Math.PI,
|
||||||
@@ -100,20 +107,22 @@ const StateWorking: React.FC<StateWorkingProps> = ({ id, type, position, rotatio
|
|||||||
transform: `rotateX(${rotationDegrees.x}deg) rotateY(${rotationDegrees.y}deg) rotateZ(${rotationDegrees.z}deg)`,
|
transform: `rotateX(${rotationDegrees.x}deg) rotateY(${rotationDegrees.y}deg) rotateZ(${rotationDegrees.z}deg)`,
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<Html position={[position[0], position[1], position[2]]}
|
<Html
|
||||||
|
position={[position[0], position[1], position[2]]}
|
||||||
scale={[0.5, 0.5, 0.5]}
|
scale={[0.5, 0.5, 0.5]}
|
||||||
transform
|
transform
|
||||||
zIndexRange={[1, 0]}
|
zIndexRange={[1, 0]}
|
||||||
sprite
|
sprite
|
||||||
style={{
|
style={{
|
||||||
transform: transformStyle.transform,
|
transform: transformStyle.transform,
|
||||||
transformStyle: 'preserve-3d',
|
transformStyle: "preserve-3d",
|
||||||
transition: 'transform 0.1s ease-out'
|
transition: "transform 0.1s ease-out",
|
||||||
|
|
||||||
}}
|
}}
|
||||||
className={`${selectedChartId?.id === id ? "activeChart" : ""}`}
|
|
||||||
>
|
>
|
||||||
<div className="stateWorking-wrapper card"
|
<div
|
||||||
|
className={`stateWorking-wrapper card ${
|
||||||
|
selectedChartId?.id === id ? "activeChart" : ""
|
||||||
|
}`}
|
||||||
onClick={() => setSelectedChartId({ id: id, type: type })}
|
onClick={() => setSelectedChartId({ id: id, type: type })}
|
||||||
onContextMenu={onContextMenu}
|
onContextMenu={onContextMenu}
|
||||||
>
|
>
|
||||||
@@ -121,12 +130,10 @@ const StateWorking: React.FC<StateWorkingProps> = ({ id, type, position, rotatio
|
|||||||
<div className="header">
|
<div className="header">
|
||||||
<span>State</span>
|
<span>State</span>
|
||||||
<span>
|
<span>
|
||||||
{datas?.input1 ? datas.input1 : 'input1'} <span>.</span>
|
{datas?.input1 ? datas.input1 : "input1"} <span>.</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="img">
|
<div className="img">{/* <img src={image} alt="" /> */}</div>
|
||||||
{/* <img src={image} alt="" /> */}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
{/* Data */}
|
{/* Data */}
|
||||||
<div className="data-wrapper">
|
<div className="data-wrapper">
|
||||||
@@ -137,28 +144,52 @@ const StateWorking: React.FC<StateWorkingProps> = ({ id, type, position, rotatio
|
|||||||
</div>
|
</div>
|
||||||
))} */}
|
))} */}
|
||||||
<div className="data-table">
|
<div className="data-table">
|
||||||
<div className="data">{measurements?.input2?.fields ? measurements.input2.fields : 'input2'}</div>
|
<div className="data">
|
||||||
<div className="key">{datas?.input2 ? datas.input2 : 'data'}</div>
|
{measurements?.input2?.fields
|
||||||
|
? measurements.input2.fields
|
||||||
|
: "input2"}
|
||||||
|
</div>
|
||||||
|
<div className="key">{datas?.input2 ? datas.input2 : "data"}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="data-table">
|
<div className="data-table">
|
||||||
<div className="data">{measurements?.input3?.fields ? measurements.input3.fields : 'input3'}</div>
|
<div className="data">
|
||||||
<div className="key">{datas?.input3 ? datas.input3 : 'data'}</div>
|
{measurements?.input3?.fields
|
||||||
|
? measurements.input3.fields
|
||||||
|
: "input3"}
|
||||||
|
</div>
|
||||||
|
<div className="key">{datas?.input3 ? datas.input3 : "data"}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="data-table">
|
<div className="data-table">
|
||||||
<div className="data">{measurements?.input4?.fields ? measurements.input4.fields : 'input4'}</div>
|
<div className="data">
|
||||||
<div className="key">{datas?.input4 ? datas.input4 : 'data'}</div>
|
{measurements?.input4?.fields
|
||||||
|
? measurements.input4.fields
|
||||||
|
: "input4"}
|
||||||
|
</div>
|
||||||
|
<div className="key">{datas?.input4 ? datas.input4 : "data"}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="data-table">
|
<div className="data-table">
|
||||||
<div className="data">{measurements?.input5?.fields ? measurements.input5.fields : 'input5'}</div>
|
<div className="data">
|
||||||
<div className="key">{datas?.input5 ? datas.input5 : 'data'}</div>
|
{measurements?.input5?.fields
|
||||||
|
? measurements.input5.fields
|
||||||
|
: "input5"}
|
||||||
|
</div>
|
||||||
|
<div className="key">{datas?.input5 ? datas.input5 : "data"}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="data-table">
|
<div className="data-table">
|
||||||
<div className="data">{measurements?.input6?.fields ? measurements.input6.fields : 'input6'}</div>
|
<div className="data">
|
||||||
<div className="key">{datas?.input6 ? datas.input6 : 'data'}</div>
|
{measurements?.input6?.fields
|
||||||
|
? measurements.input6.fields
|
||||||
|
: "input6"}
|
||||||
|
</div>
|
||||||
|
<div className="key">{datas?.input6 ? datas.input6 : "data"}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="data-table">
|
<div className="data-table">
|
||||||
<div className="data">{measurements?.input7?.fields ? measurements.input7.fields : 'input7'}</div>
|
<div className="data">
|
||||||
<div className="key">{datas?.input7 ? datas.input7 : 'data'}</div>
|
{measurements?.input7?.fields
|
||||||
|
? measurements.input7.fields
|
||||||
|
: "input7"}
|
||||||
|
</div>
|
||||||
|
<div className="key">{datas?.input7 ? datas.input7 : "data"}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -167,5 +198,3 @@ const StateWorking: React.FC<StateWorkingProps> = ({ id, type, position, rotatio
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default StateWorking;
|
export default StateWorking;
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -49,20 +49,32 @@ interface ThroughputProps {
|
|||||||
onContextMenu?: (event: React.MouseEvent) => void;
|
onContextMenu?: (event: React.MouseEvent) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Throughput: React.FC<ThroughputProps> = ({ id, type, position, rotation, onContextMenu }) => {
|
const Throughput: React.FC<ThroughputProps> = ({
|
||||||
|
id,
|
||||||
|
type,
|
||||||
|
position,
|
||||||
|
rotation,
|
||||||
|
onContextMenu,
|
||||||
|
}) => {
|
||||||
const { selectedChartId, setSelectedChartId } = useWidgetStore();
|
const { selectedChartId, setSelectedChartId } = useWidgetStore();
|
||||||
const { measurements: chartMeasurements, duration: chartDuration, name: widgetName } = useChartStore();
|
const {
|
||||||
|
measurements: chartMeasurements,
|
||||||
|
duration: chartDuration,
|
||||||
|
name: widgetName,
|
||||||
|
} = useChartStore();
|
||||||
const [measurements, setmeasurements] = useState<any>({});
|
const [measurements, setmeasurements] = useState<any>({});
|
||||||
const [duration, setDuration] = useState("1h")
|
const [duration, setDuration] = useState("1h");
|
||||||
const [name, setName] = useState("Widget")
|
const [name, setName] = useState("Widget");
|
||||||
const [chartData, setChartData] = useState<{ labels: string[]; datasets: any[] }>({
|
const [chartData, setChartData] = useState<{
|
||||||
|
labels: string[];
|
||||||
|
datasets: any[];
|
||||||
|
}>({
|
||||||
labels: [],
|
labels: [],
|
||||||
datasets: [],
|
datasets: [],
|
||||||
});
|
});
|
||||||
const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL;
|
const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL;
|
||||||
const email = localStorage.getItem("email") || "";
|
const email = localStorage.getItem("email") || "";
|
||||||
const organization = email?.split("@")[1]?.split(".")[0]
|
const organization = email?.split("@")[1]?.split(".")[0];
|
||||||
|
|
||||||
// Sample data for the line graph
|
// Sample data for the line graph
|
||||||
const graphData: ChartData<"line"> = {
|
const graphData: ChartData<"line"> = {
|
||||||
@@ -112,7 +124,8 @@ const Throughput: React.FC<ThroughputProps> = ({ id, type, position, rotation, o
|
|||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!iotApiUrl || !measurements || Object.keys(measurements).length === 0) return;
|
if (!iotApiUrl || !measurements || Object.keys(measurements).length === 0)
|
||||||
|
return;
|
||||||
|
|
||||||
const socket = io(`http://${iotApiUrl}`);
|
const socket = io(`http://${iotApiUrl}`);
|
||||||
|
|
||||||
@@ -122,7 +135,6 @@ const Throughput: React.FC<ThroughputProps> = ({ id, type, position, rotation, o
|
|||||||
interval: 1000,
|
interval: 1000,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const startStream = () => {
|
const startStream = () => {
|
||||||
socket.emit("lineInput", inputData);
|
socket.emit("lineInput", inputData);
|
||||||
};
|
};
|
||||||
@@ -157,14 +169,15 @@ const Throughput: React.FC<ThroughputProps> = ({ id, type, position, rotation, o
|
|||||||
}, [measurements, duration, iotApiUrl]);
|
}, [measurements, duration, iotApiUrl]);
|
||||||
|
|
||||||
const fetchSavedInputes = async () => {
|
const fetchSavedInputes = async () => {
|
||||||
|
|
||||||
if (id !== "") {
|
if (id !== "") {
|
||||||
try {
|
try {
|
||||||
const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/widget3D/${id}/${organization}`);
|
const response = await axios.get(
|
||||||
|
`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/widget3D/${id}/${organization}`
|
||||||
|
);
|
||||||
if (response.status === 200) {
|
if (response.status === 200) {
|
||||||
setmeasurements(response.data.Data.measurements)
|
setmeasurements(response.data.Data.measurements);
|
||||||
setDuration(response.data.Data.duration)
|
setDuration(response.data.Data.duration);
|
||||||
setName(response.data.widgetName)
|
setName(response.data.widgetName);
|
||||||
} else {
|
} else {
|
||||||
console.log("Unexpected response:", response);
|
console.log("Unexpected response:", response);
|
||||||
}
|
}
|
||||||
@@ -172,7 +185,7 @@ const Throughput: React.FC<ThroughputProps> = ({ id, type, position, rotation, o
|
|||||||
console.error("There was an error!", error);
|
console.error("There was an error!", error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchSavedInputes();
|
fetchSavedInputes();
|
||||||
@@ -182,8 +195,7 @@ const Throughput: React.FC<ThroughputProps> = ({ id, type, position, rotation, o
|
|||||||
if (selectedChartId?.id === id) {
|
if (selectedChartId?.id === id) {
|
||||||
fetchSavedInputes();
|
fetchSavedInputes();
|
||||||
}
|
}
|
||||||
}
|
}, [chartMeasurements, chartDuration, widgetName]);
|
||||||
, [chartMeasurements, chartDuration, widgetName])
|
|
||||||
const rotationDegrees = {
|
const rotationDegrees = {
|
||||||
x: (rotation[0] * 180) / Math.PI,
|
x: (rotation[0] * 180) / Math.PI,
|
||||||
y: (rotation[1] * 180) / Math.PI,
|
y: (rotation[1] * 180) / Math.PI,
|
||||||
@@ -195,20 +207,22 @@ const Throughput: React.FC<ThroughputProps> = ({ id, type, position, rotation, o
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Html position={[position[0], position[1], position[2]]}
|
<Html
|
||||||
|
position={[position[0], position[1], position[2]]}
|
||||||
scale={[0.5, 0.5, 0.5]}
|
scale={[0.5, 0.5, 0.5]}
|
||||||
transform
|
transform
|
||||||
zIndexRange={[1, 0]}
|
zIndexRange={[1, 0]}
|
||||||
sprite
|
sprite
|
||||||
style={{
|
style={{
|
||||||
transform: transformStyle.transform,
|
transform: transformStyle.transform,
|
||||||
transformStyle: 'preserve-3d',
|
transformStyle: "preserve-3d",
|
||||||
transition: 'transform 0.1s ease-out'
|
transition: "transform 0.1s ease-out",
|
||||||
|
|
||||||
}}
|
}}
|
||||||
className={`${selectedChartId?.id === id ? "activeChart" : ""}`}
|
|
||||||
>
|
>
|
||||||
<div className="throughput-wrapper"
|
<div
|
||||||
|
className={`throughput-wrapper card ${
|
||||||
|
selectedChartId?.id === id ? "activeChart" : ""
|
||||||
|
}`}
|
||||||
onClick={() => setSelectedChartId({ id: id, type: type })}
|
onClick={() => setSelectedChartId({ id: id, type: type })}
|
||||||
onContextMenu={onContextMenu}
|
onContextMenu={onContextMenu}
|
||||||
>
|
>
|
||||||
@@ -235,7 +249,10 @@ const Throughput: React.FC<ThroughputProps> = ({ id, type, position, rotation, o
|
|||||||
</div>
|
</div>
|
||||||
<div className="line-graph">
|
<div className="line-graph">
|
||||||
{/* Line graph using react-chartjs-2 */}
|
{/* Line graph using react-chartjs-2 */}
|
||||||
<LineGraphComponent data={Object.keys(measurements).length > 0 ? chartData : graphData} options={graphOptions} />
|
<LineGraphComponent
|
||||||
|
data={Object.keys(measurements).length > 0 ? chartData : graphData}
|
||||||
|
options={graphOptions}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="footer">
|
<div className="footer">
|
||||||
You made an extra <span className="value">$1256.13</span> this month
|
You made an extra <span className="value">$1256.13</span> this month
|
||||||
|
|||||||
@@ -148,7 +148,7 @@ const Assets: React.FC = () => {
|
|||||||
<div className="assets-container">
|
<div className="assets-container">
|
||||||
{categoryAssets &&
|
{categoryAssets &&
|
||||||
categoryAssets?.map((asset: any, index: number) => (
|
categoryAssets?.map((asset: any, index: number) => (
|
||||||
<div key={index} className="assets">
|
<div key={index} className="assets" id={asset.filename}>
|
||||||
<img
|
<img
|
||||||
src={asset?.thumbnail}
|
src={asset?.thumbnail}
|
||||||
alt={asset.filename}
|
alt={asset.filename}
|
||||||
@@ -172,6 +172,7 @@ const Assets: React.FC = () => {
|
|||||||
<div className="assets-wrapper">
|
<div className="assets-wrapper">
|
||||||
<div
|
<div
|
||||||
className="back-button"
|
className="back-button"
|
||||||
|
id="asset-backButtom"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setSelectedCategory(null);
|
setSelectedCategory(null);
|
||||||
setCategoryAssets([]);
|
setCategoryAssets([]);
|
||||||
@@ -183,7 +184,7 @@ const Assets: React.FC = () => {
|
|||||||
<div className="assets-container">
|
<div className="assets-container">
|
||||||
{categoryAssets &&
|
{categoryAssets &&
|
||||||
categoryAssets?.map((asset: any, index: number) => (
|
categoryAssets?.map((asset: any, index: number) => (
|
||||||
<div key={index} className="assets">
|
<div key={index} className="assets" id={asset.filename}>
|
||||||
<img
|
<img
|
||||||
src={asset?.thumbnail}
|
src={asset?.thumbnail}
|
||||||
alt={asset.filename}
|
alt={asset.filename}
|
||||||
@@ -223,6 +224,7 @@ const Assets: React.FC = () => {
|
|||||||
<div
|
<div
|
||||||
key={index}
|
key={index}
|
||||||
className="category"
|
className="category"
|
||||||
|
id={category}
|
||||||
onClick={() => fetchCategoryAssets(category)}
|
onClick={() => fetchCategoryAssets(category)}
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import useTemplateStore from "../../../../store/useTemplateStore";
|
|||||||
import { useSelectedZoneStore } from "../../../../store/useZoneStore";
|
import { useSelectedZoneStore } from "../../../../store/useZoneStore";
|
||||||
import { getTemplateData } from "../../../../services/realTimeVisulization/zoneData/getTemplate";
|
import { getTemplateData } from "../../../../services/realTimeVisulization/zoneData/getTemplate";
|
||||||
import { useSocketStore } from "../../../../store/store";
|
import { useSocketStore } from "../../../../store/store";
|
||||||
|
import RenameInput from "../../../ui/inputs/RenameInput";
|
||||||
|
|
||||||
const Templates = () => {
|
const Templates = () => {
|
||||||
const { templates, removeTemplate, setTemplates } = useTemplateStore();
|
const { templates, removeTemplate, setTemplates } = useTemplateStore();
|
||||||
@@ -34,7 +35,10 @@ const Templates = () => {
|
|||||||
templateID: id,
|
templateID: id,
|
||||||
};
|
};
|
||||||
if (visualizationSocket) {
|
if (visualizationSocket) {
|
||||||
visualizationSocket.emit("v2:viz-template:deleteTemplate", deleteTemplate);
|
visualizationSocket.emit(
|
||||||
|
"v2:viz-template:deleteTemplate",
|
||||||
|
deleteTemplate
|
||||||
|
);
|
||||||
}
|
}
|
||||||
removeTemplate(id);
|
removeTemplate(id);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -65,11 +69,15 @@ const Templates = () => {
|
|||||||
widgets: template.widgets,
|
widgets: template.widgets,
|
||||||
});
|
});
|
||||||
|
|
||||||
useDroppedObjectsStore.getState().setZone(selectedZone.zoneName, selectedZone.zoneId);
|
useDroppedObjectsStore
|
||||||
|
.getState()
|
||||||
|
.setZone(selectedZone.zoneName, selectedZone.zoneId);
|
||||||
|
|
||||||
if (Array.isArray(template.floatingWidget)) {
|
if (Array.isArray(template.floatingWidget)) {
|
||||||
template.floatingWidget.forEach((val: any) => {
|
template.floatingWidget.forEach((val: any) => {
|
||||||
useDroppedObjectsStore.getState().addObject(selectedZone.zoneName, val);
|
useDroppedObjectsStore
|
||||||
|
.getState()
|
||||||
|
.addObject(selectedZone.zoneName, val);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -79,7 +87,7 @@ const Templates = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="template-list">
|
<div className="template-list">
|
||||||
{templates.map((template) => (
|
{templates.map((template, index) => (
|
||||||
<div key={template.id} className="template-item">
|
<div key={template.id} className="template-item">
|
||||||
{template?.snapshot && (
|
{template?.snapshot && (
|
||||||
<div className="template-image-container">
|
<div className="template-image-container">
|
||||||
@@ -96,7 +104,8 @@ const Templates = () => {
|
|||||||
onClick={() => handleLoadTemplate(template)}
|
onClick={() => handleLoadTemplate(template)}
|
||||||
className="template-name"
|
className="template-name"
|
||||||
>
|
>
|
||||||
{template.name}
|
{/* {`Template ${index + 1}`} */}
|
||||||
|
<RenameInput value={`Template ${index + 1}`} />
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
onClick={() => handleDeleteTemplate(template.id)}
|
onClick={() => handleDeleteTemplate(template.id)}
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
import React, { useEffect } from "react";
|
import React from "react";
|
||||||
import {
|
import {
|
||||||
CleanPannel,
|
CleanPannel,
|
||||||
EyeIcon,
|
EyeIcon,
|
||||||
LockIcon,
|
LockIcon,
|
||||||
} from "../../icons/RealTimeVisulationIcons";
|
} from "../../icons/RealTimeVisulationIcons";
|
||||||
import { panelData } from "../../../services/realTimeVisulization/zoneData/panel";
|
|
||||||
import { AddIcon } from "../../icons/ExportCommonIcons";
|
import { AddIcon } from "../../icons/ExportCommonIcons";
|
||||||
import { deletePanelApi } from "../../../services/realTimeVisulization/zoneData/deletePanel";
|
|
||||||
import { useSocketStore } from "../../../store/store";
|
import { useSocketStore } from "../../../store/store";
|
||||||
import { clearPanel } from "../../../services/realTimeVisulization/zoneData/clearPanel";
|
import { clearPanel } from "../../../services/realTimeVisulization/zoneData/clearPanel";
|
||||||
import { lockPanel } from "../../../services/realTimeVisulization/zoneData/lockPanel";
|
import { lockPanel } from "../../../services/realTimeVisulization/zoneData/lockPanel";
|
||||||
@@ -14,6 +12,11 @@ import { lockPanel } from "../../../services/realTimeVisulization/zoneData/lockP
|
|||||||
// Define the type for `Side`
|
// Define the type for `Side`
|
||||||
type Side = "top" | "bottom" | "left" | "right";
|
type Side = "top" | "bottom" | "left" | "right";
|
||||||
|
|
||||||
|
// Define the type for HiddenPanels, where keys are zone IDs and values are arrays of hidden sides
|
||||||
|
interface HiddenPanels {
|
||||||
|
[zoneId: string]: Side[];
|
||||||
|
}
|
||||||
|
|
||||||
// Define the type for the props passed to the Buttons component
|
// Define the type for the props passed to the Buttons component
|
||||||
interface ButtonsProps {
|
interface ButtonsProps {
|
||||||
selectedZone: {
|
selectedZone: {
|
||||||
@@ -37,7 +40,6 @@ interface ButtonsProps {
|
|||||||
zoneName: string;
|
zoneName: string;
|
||||||
activeSides: Side[];
|
activeSides: Side[];
|
||||||
panelOrder: Side[];
|
panelOrder: Side[];
|
||||||
|
|
||||||
lockedPanels: Side[];
|
lockedPanels: Side[];
|
||||||
zoneId: string;
|
zoneId: string;
|
||||||
zoneViewPortTarget: number[];
|
zoneViewPortTarget: number[];
|
||||||
@@ -51,8 +53,8 @@ interface ButtonsProps {
|
|||||||
}[];
|
}[];
|
||||||
}>
|
}>
|
||||||
>;
|
>;
|
||||||
hiddenPanels: Side[]; // Add this prop for hidden panels
|
hiddenPanels: HiddenPanels; // Updated prop type
|
||||||
setHiddenPanels: React.Dispatch<React.SetStateAction<Side[]>>; // Add this prop for updating hidden panels
|
setHiddenPanels: React.Dispatch<React.SetStateAction<HiddenPanels>>; // Updated prop type
|
||||||
}
|
}
|
||||||
|
|
||||||
const AddButtons: React.FC<ButtonsProps> = ({
|
const AddButtons: React.FC<ButtonsProps> = ({
|
||||||
@@ -63,11 +65,33 @@ const AddButtons: React.FC<ButtonsProps> = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const { visualizationSocket } = useSocketStore();
|
const { visualizationSocket } = useSocketStore();
|
||||||
|
|
||||||
// Local state to track hidden panels
|
// Function to toggle visibility of a panel
|
||||||
|
const toggleVisibility = (side: Side) => {
|
||||||
|
const isHidden = hiddenPanels[selectedZone.zoneId]?.includes(side) ?? false;
|
||||||
|
|
||||||
|
if (isHidden) {
|
||||||
|
// If the panel is already hidden, remove it from the hiddenPanels array for this zone
|
||||||
|
setHiddenPanels((prevHiddenPanels) => ({
|
||||||
|
...prevHiddenPanels,
|
||||||
|
[selectedZone.zoneId]: prevHiddenPanels[selectedZone.zoneId].filter(
|
||||||
|
(panel) => panel !== side
|
||||||
|
),
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
// If the panel is visible, add it to the hiddenPanels array for this zone
|
||||||
|
setHiddenPanels((prevHiddenPanels) => ({
|
||||||
|
...prevHiddenPanels,
|
||||||
|
[selectedZone.zoneId]: [
|
||||||
|
...(prevHiddenPanels[selectedZone.zoneId] || []),
|
||||||
|
side,
|
||||||
|
],
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Function to toggle lock/unlock a panel
|
// Function to toggle lock/unlock a panel
|
||||||
const toggleLockPanel = async (side: Side) => {
|
const toggleLockPanel = async (side: Side) => {
|
||||||
console.log('side: ', side);
|
// console.log('side: ', side);
|
||||||
const email = localStorage.getItem("email") || "";
|
const email = localStorage.getItem("email") || "";
|
||||||
const organization = email?.split("@")[1]?.split(".")[0]; // Fallback value
|
const organization = email?.split("@")[1]?.split(".")[0]; // Fallback value
|
||||||
//add api
|
//add api
|
||||||
@@ -99,23 +123,10 @@ const AddButtons: React.FC<ButtonsProps> = ({
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Function to toggle visibility of a panel
|
|
||||||
const toggleVisibility = (side: Side) => {
|
|
||||||
|
|
||||||
const isHidden = hiddenPanels.includes(side);
|
|
||||||
if (isHidden) {
|
|
||||||
// If the panel is already hidden, remove it from the hiddenPanels array
|
|
||||||
setHiddenPanels(hiddenPanels.filter((panel) => panel !== side));
|
|
||||||
} else {
|
|
||||||
// If the panel is visible, add it to the hiddenPanels array
|
|
||||||
setHiddenPanels([...hiddenPanels, side]);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Function to clean all widgets from a panel
|
// Function to clean all widgets from a panel
|
||||||
const cleanPanel = async (side: Side) => {
|
const cleanPanel = async (side: Side) => {
|
||||||
//add api
|
//add api
|
||||||
console.log('side: ', side);
|
// console.log('side: ', side);
|
||||||
const email = localStorage.getItem("email") || "";
|
const email = localStorage.getItem("email") || "";
|
||||||
const organization = email?.split("@")[1]?.split(".")[0]; // Fallback value
|
const organization = email?.split("@")[1]?.split(".")[0]; // Fallback value
|
||||||
|
|
||||||
@@ -135,7 +146,7 @@ const AddButtons: React.FC<ButtonsProps> = ({
|
|||||||
widgets: cleanedWidgets,
|
widgets: cleanedWidgets,
|
||||||
};
|
};
|
||||||
// Update the selectedZone state
|
// Update the selectedZone state
|
||||||
console.log('updatedZone: ', updatedZone);
|
// console.log('updatedZone: ', updatedZone);
|
||||||
setSelectedZone(updatedZone);
|
setSelectedZone(updatedZone);
|
||||||
|
|
||||||
// let response = await clearPanel(selectedZone.zoneId, organization, side)
|
// let response = await clearPanel(selectedZone.zoneId, organization, side)
|
||||||
@@ -196,8 +207,6 @@ const AddButtons: React.FC<ButtonsProps> = ({
|
|||||||
//
|
//
|
||||||
// }
|
// }
|
||||||
} else {
|
} else {
|
||||||
setHiddenPanels(hiddenPanels.filter((panel) => panel !== side));
|
|
||||||
|
|
||||||
// Panel does not exist: Create panel
|
// Panel does not exist: Create panel
|
||||||
try {
|
try {
|
||||||
// Get email and organization safely with a default fallback
|
// Get email and organization safely with a default fallback
|
||||||
@@ -234,7 +243,6 @@ const AddButtons: React.FC<ButtonsProps> = ({
|
|||||||
} catch (error) { }
|
} catch (error) { }
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div>
|
<div>
|
||||||
@@ -261,16 +269,21 @@ const AddButtons: React.FC<ButtonsProps> = ({
|
|||||||
<div className="extra-Bs">
|
<div className="extra-Bs">
|
||||||
{/* Hide Panel */}
|
{/* Hide Panel */}
|
||||||
<div
|
<div
|
||||||
className={`icon ${hiddenPanels.includes(side) ? "active" : ""
|
className={`icon ${
|
||||||
|
hiddenPanels[selectedZone.zoneId]?.includes(side)
|
||||||
|
? "active"
|
||||||
|
: ""
|
||||||
}`}
|
}`}
|
||||||
title={
|
title={
|
||||||
hiddenPanels.includes(side) ? "Show Panel" : "Hide Panel"
|
hiddenPanels[selectedZone.zoneId]?.includes(side)
|
||||||
|
? "Show Panel"
|
||||||
|
: "Hide Panel"
|
||||||
}
|
}
|
||||||
onClick={() => toggleVisibility(side)}
|
onClick={() => toggleVisibility(side)}
|
||||||
>
|
>
|
||||||
<EyeIcon
|
<EyeIcon
|
||||||
fill={
|
fill={
|
||||||
hiddenPanels.includes(side)
|
hiddenPanels[selectedZone.zoneId]?.includes(side)
|
||||||
? "var(--primary-color)"
|
? "var(--primary-color)"
|
||||||
: "var(--text-color)"
|
: "var(--text-color)"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -88,12 +88,15 @@ export const DraggableWidget = ({
|
|||||||
|
|
||||||
const chartWidget = useRef<HTMLDivElement>(null);
|
const chartWidget = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
const isPanelHidden = hiddenPanels.includes(widget.panel);
|
OuterClick({
|
||||||
|
contextClassName: [
|
||||||
// OuterClick({
|
"chart-container",
|
||||||
// contextClassName: ["chart-container", "floating", "sidebar-right-wrapper"],
|
"floating",
|
||||||
// setMenuVisible: () => setSelectedChartId(null),
|
"sidebar-right-wrapper",
|
||||||
// });
|
"card",
|
||||||
|
],
|
||||||
|
setMenuVisible: () => setSelectedChartId(null),
|
||||||
|
});
|
||||||
|
|
||||||
const deleteSelectedChart = async () => {
|
const deleteSelectedChart = async () => {
|
||||||
try {
|
try {
|
||||||
@@ -397,4 +400,4 @@ export const DraggableWidget = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// by using canvasDimensions.height canvasDimensions.width dynamically div value insted of static 6 and 4 calculate according to canvasDimensions.width canvasDimensions.height
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ interface PanelProps {
|
|||||||
widgets: Widget[];
|
widgets: Widget[];
|
||||||
}>
|
}>
|
||||||
>;
|
>;
|
||||||
hiddenPanels: string[];
|
hiddenPanels: any;
|
||||||
setZonesData: React.Dispatch<React.SetStateAction<any>>;
|
setZonesData: React.Dispatch<React.SetStateAction<any>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,7 +139,12 @@ const Panel: React.FC<PanelProps> = ({
|
|||||||
const handleDrop = (e: React.DragEvent, panel: Side) => {
|
const handleDrop = (e: React.DragEvent, panel: Side) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const { draggedAsset } = useWidgetStore.getState();
|
const { draggedAsset } = useWidgetStore.getState();
|
||||||
if (!draggedAsset || isPanelLocked(panel)) return;
|
if (
|
||||||
|
!draggedAsset ||
|
||||||
|
isPanelLocked(panel) ||
|
||||||
|
hiddenPanels[selectedZone.zoneId]?.includes(panel)
|
||||||
|
)
|
||||||
|
return;
|
||||||
|
|
||||||
const currentWidgetsCount = getCurrentWidgetCount(panel);
|
const currentWidgetsCount = getCurrentWidgetCount(panel);
|
||||||
const maxCapacity = calculatePanelCapacity(panel);
|
const maxCapacity = calculatePanelCapacity(panel);
|
||||||
@@ -278,7 +283,7 @@ const Panel: React.FC<PanelProps> = ({
|
|||||||
key={side}
|
key={side}
|
||||||
id="panel-wrapper"
|
id="panel-wrapper"
|
||||||
className={`panel ${side}-panel absolute ${
|
className={`panel ${side}-panel absolute ${
|
||||||
hiddenPanels.includes(side) ? "hidePanel" : ""
|
hiddenPanels[selectedZone.zoneId]?.includes(side) ? "hidePanel" : ""
|
||||||
}`}
|
}`}
|
||||||
style={getPanelStyle(side)}
|
style={getPanelStyle(side)}
|
||||||
onDrop={(e) => handleDrop(e, side)}
|
onDrop={(e) => handleDrop(e, side)}
|
||||||
@@ -294,7 +299,9 @@ const Panel: React.FC<PanelProps> = ({
|
|||||||
<div
|
<div
|
||||||
className={`panel-content ${isPlaying && "fullScreen"}`}
|
className={`panel-content ${isPlaying && "fullScreen"}`}
|
||||||
style={{
|
style={{
|
||||||
pointerEvents: selectedZone.lockedPanels.includes(side)
|
pointerEvents:
|
||||||
|
selectedZone.lockedPanels.includes(side) ||
|
||||||
|
hiddenPanels[selectedZone.zoneId]?.includes(side)
|
||||||
? "none"
|
? "none"
|
||||||
: "auto",
|
: "auto",
|
||||||
opacity: selectedZone.lockedPanels.includes(side) ? "0.8" : "1",
|
opacity: selectedZone.lockedPanels.includes(side) ? "0.8" : "1",
|
||||||
|
|||||||
@@ -53,8 +53,13 @@ type Widget = {
|
|||||||
data: any;
|
data: any;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Define the type for HiddenPanels, where keys are zone IDs and values are arrays of hidden sides
|
||||||
|
interface HiddenPanels {
|
||||||
|
[zoneId: string]: Side[];
|
||||||
|
}
|
||||||
|
|
||||||
const RealTimeVisulization: React.FC = () => {
|
const RealTimeVisulization: React.FC = () => {
|
||||||
const [hiddenPanels, setHiddenPanels] = React.useState<Side[]>([]);
|
const [hiddenPanels, setHiddenPanels] = React.useState<HiddenPanels>({});
|
||||||
const containerRef = useRef<HTMLDivElement>(null);
|
const containerRef = useRef<HTMLDivElement>(null);
|
||||||
const { isPlaying } = usePlayButtonStore();
|
const { isPlaying } = usePlayButtonStore();
|
||||||
const { activeModule } = useModuleStore();
|
const { activeModule } = useModuleStore();
|
||||||
@@ -74,7 +79,6 @@ const RealTimeVisulization: React.FC = () => {
|
|||||||
const { widgetSubOption, setWidgetSubOption } = useWidgetSubOption();
|
const { widgetSubOption, setWidgetSubOption } = useWidgetSubOption();
|
||||||
const { visualizationSocket } = useSocketStore();
|
const { visualizationSocket } = useSocketStore();
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
async function GetZoneData() {
|
async function GetZoneData() {
|
||||||
const email = localStorage.getItem("email") || "";
|
const email = localStorage.getItem("email") || "";
|
||||||
@@ -229,8 +233,6 @@ const RealTimeVisulization: React.FC = () => {
|
|||||||
};
|
};
|
||||||
}, [setRightClickSelected]);
|
}, [setRightClickSelected]);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div
|
<div
|
||||||
@@ -263,7 +265,6 @@ const RealTimeVisulization: React.FC = () => {
|
|||||||
}}
|
}}
|
||||||
onDrop={(event) => handleDrop(event)}
|
onDrop={(event) => handleDrop(event)}
|
||||||
onDragOver={(event) => event.preventDefault()}
|
onDragOver={(event) => event.preventDefault()}
|
||||||
|
|
||||||
>
|
>
|
||||||
<Scene />
|
<Scene />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -2,12 +2,23 @@ import React, { useState, useEffect, useMemo, useRef } from "react";
|
|||||||
import { Line, Sphere } from "@react-three/drei";
|
import { Line, Sphere } from "@react-three/drei";
|
||||||
import { useThree, useFrame } from "@react-three/fiber";
|
import { useThree, useFrame } from "@react-three/fiber";
|
||||||
import * as THREE from "three";
|
import * as THREE from "three";
|
||||||
import { useActiveLayer, useDeleteTool, useDeletePointOrLine, useMovePoint, useSocketStore, useToggleView, useToolMode, useRemovedLayer, useZones, useZonePoints } from "../../../store/store";
|
import {
|
||||||
|
useActiveLayer,
|
||||||
|
useDeleteTool,
|
||||||
|
useDeletePointOrLine,
|
||||||
|
useMovePoint,
|
||||||
|
useSocketStore,
|
||||||
|
useToggleView,
|
||||||
|
useToolMode,
|
||||||
|
useRemovedLayer,
|
||||||
|
useZones,
|
||||||
|
useZonePoints,
|
||||||
|
} from "../../../store/store";
|
||||||
// import { setZonesApi } from "../../../services/factoryBuilder/zones/setZonesApi";
|
// import { setZonesApi } from "../../../services/factoryBuilder/zones/setZonesApi";
|
||||||
// import { deleteZonesApi } from "../../../services/factoryBuilder/zones/deleteZoneApi";
|
// import { deleteZonesApi } from "../../../services/factoryBuilder/zones/deleteZoneApi";
|
||||||
import { getZonesApi } from "../../../services/factoryBuilder/zones/getZonesApi";
|
import { getZonesApi } from "../../../services/factoryBuilder/zones/getZonesApi";
|
||||||
|
|
||||||
import * as CONSTANTS from '../../../types/world/worldConstants';
|
import * as CONSTANTS from "../../../types/world/worldConstants";
|
||||||
|
|
||||||
const ZoneGroup: React.FC = () => {
|
const ZoneGroup: React.FC = () => {
|
||||||
const { camera, pointer, gl, raycaster, scene, controls } = useThree();
|
const { camera, pointer, gl, raycaster, scene, controls } = useThree();
|
||||||
@@ -16,8 +27,13 @@ const ZoneGroup: React.FC = () => {
|
|||||||
const { zones, setZones } = useZones();
|
const { zones, setZones } = useZones();
|
||||||
const { zonePoints, setZonePoints } = useZonePoints();
|
const { zonePoints, setZonePoints } = useZonePoints();
|
||||||
const [isDragging, setIsDragging] = useState(false);
|
const [isDragging, setIsDragging] = useState(false);
|
||||||
const [draggedSphere, setDraggedSphere] = useState<THREE.Vector3 | null>(null);
|
const [draggedSphere, setDraggedSphere] = useState<THREE.Vector3 | null>(
|
||||||
const plane = useMemo(() => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0), []);
|
null
|
||||||
|
);
|
||||||
|
const plane = useMemo(
|
||||||
|
() => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0),
|
||||||
|
[]
|
||||||
|
);
|
||||||
const { toggleView } = useToggleView();
|
const { toggleView } = useToggleView();
|
||||||
const { deletePointOrLine, setDeletePointOrLine } = useDeletePointOrLine();
|
const { deletePointOrLine, setDeletePointOrLine } = useDeletePointOrLine();
|
||||||
const { removedLayer, setRemovedLayer } = useRemovedLayer();
|
const { removedLayer, setRemovedLayer } = useRemovedLayer();
|
||||||
@@ -29,7 +45,9 @@ const ZoneGroup: React.FC = () => {
|
|||||||
|
|
||||||
const groupsRef = useRef<any>();
|
const groupsRef = useRef<any>();
|
||||||
|
|
||||||
const zoneMaterial = useMemo(() => new THREE.ShaderMaterial({
|
const zoneMaterial = useMemo(
|
||||||
|
() =>
|
||||||
|
new THREE.ShaderMaterial({
|
||||||
side: THREE.DoubleSide,
|
side: THREE.DoubleSide,
|
||||||
vertexShader: `
|
vertexShader: `
|
||||||
varying vec2 vUv;
|
varying vec2 vUv;
|
||||||
@@ -55,7 +73,7 @@ const ZoneGroup: React.FC = () => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchZones = async () => {
|
const fetchZones = async () => {
|
||||||
const email = localStorage.getItem('email');
|
const email = localStorage.getItem("email");
|
||||||
if (!email) return;
|
if (!email) return;
|
||||||
|
|
||||||
const organization = email.split("@")[1].split(".")[0];
|
const organization = email.split("@")[1].split(".")[0];
|
||||||
@@ -68,13 +86,17 @@ const ZoneGroup: React.FC = () => {
|
|||||||
points: zone.points,
|
points: zone.points,
|
||||||
viewPortCenter: zone.viewPortCenter,
|
viewPortCenter: zone.viewPortCenter,
|
||||||
viewPortposition: zone.viewPortposition,
|
viewPortposition: zone.viewPortposition,
|
||||||
layer: zone.layer
|
layer: zone.layer,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
setZones(fetchedZones);
|
setZones(fetchedZones);
|
||||||
|
|
||||||
const fetchedPoints = data.data.flatMap((zone: any) =>
|
const fetchedPoints = data.data.flatMap((zone: any) =>
|
||||||
zone.points.slice(0, 4).map((point: [number, number, number]) => new THREE.Vector3(...point))
|
zone.points
|
||||||
|
.slice(0, 4)
|
||||||
|
.map(
|
||||||
|
(point: [number, number, number]) => new THREE.Vector3(...point)
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
setZonePoints(fetchedPoints);
|
setZonePoints(fetchedPoints);
|
||||||
@@ -85,14 +107,14 @@ const ZoneGroup: React.FC = () => {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
localStorage.setItem("zones", JSON.stringify(zones));
|
||||||
localStorage.setItem('zones', JSON.stringify(zones));
|
}, [zones]);
|
||||||
|
|
||||||
}, [zones])
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (removedLayer) {
|
if (removedLayer) {
|
||||||
const updatedZones = zones.filter((zone: any) => zone.layer !== removedLayer);
|
const updatedZones = zones.filter(
|
||||||
|
(zone: any) => zone.layer !== removedLayer
|
||||||
|
);
|
||||||
setZones(updatedZones);
|
setZones(updatedZones);
|
||||||
|
|
||||||
const updatedzonePoints = zonePoints.filter((_: any, index: any) => {
|
const updatedzonePoints = zonePoints.filter((_: any, index: any) => {
|
||||||
@@ -101,7 +123,9 @@ const ZoneGroup: React.FC = () => {
|
|||||||
});
|
});
|
||||||
setZonePoints(updatedzonePoints);
|
setZonePoints(updatedzonePoints);
|
||||||
|
|
||||||
zones.filter((zone: any) => zone.layer === removedLayer).forEach((zone: any) => {
|
zones
|
||||||
|
.filter((zone: any) => zone.layer === removedLayer)
|
||||||
|
.forEach((zone: any) => {
|
||||||
deleteZoneFromBackend(zone.zoneId);
|
deleteZoneFromBackend(zone.zoneId);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -124,17 +148,22 @@ const ZoneGroup: React.FC = () => {
|
|||||||
}
|
}
|
||||||
}, [toolMode, toggleView]);
|
}, [toolMode, toggleView]);
|
||||||
|
|
||||||
|
const addZoneToBackend = async (zone: {
|
||||||
const addZoneToBackend = async (zone: { zoneId: string; zoneName: string; points: [number, number, number][]; layer: string }) => {
|
zoneId: string;
|
||||||
|
zoneName: string;
|
||||||
const email = localStorage.getItem('email');
|
points: [number, number, number][];
|
||||||
const userId = localStorage.getItem('userId');
|
layer: string;
|
||||||
const organization = (email!.split("@")[1]).split(".")[0];
|
}) => {
|
||||||
|
const email = localStorage.getItem("email");
|
||||||
|
const userId = localStorage.getItem("userId");
|
||||||
|
const organization = email!.split("@")[1].split(".")[0];
|
||||||
|
|
||||||
const calculateCenter = (points: number[][]) => {
|
const calculateCenter = (points: number[][]) => {
|
||||||
if (!points || points.length === 0) return null;
|
if (!points || points.length === 0) return null;
|
||||||
|
|
||||||
let sumX = 0, sumY = 0, sumZ = 0;
|
let sumX = 0,
|
||||||
|
sumY = 0,
|
||||||
|
sumZ = 0;
|
||||||
const numPoints = points.length;
|
const numPoints = points.length;
|
||||||
|
|
||||||
points.forEach(([x, y, z]) => {
|
points.forEach(([x, y, z]) => {
|
||||||
@@ -143,10 +172,16 @@ const ZoneGroup: React.FC = () => {
|
|||||||
sumZ += z;
|
sumZ += z;
|
||||||
});
|
});
|
||||||
|
|
||||||
return [sumX / numPoints, sumY / numPoints, sumZ / numPoints] as [number, number, number];
|
return [sumX / numPoints, sumY / numPoints, sumZ / numPoints] as [
|
||||||
|
number,
|
||||||
|
number,
|
||||||
|
number
|
||||||
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
const target: [number, number, number] | null = calculateCenter(zone.points);
|
const target: [number, number, number] | null = calculateCenter(
|
||||||
|
zone.points
|
||||||
|
);
|
||||||
if (!target) return;
|
if (!target) return;
|
||||||
const position = [target[0], 10, target[2]];
|
const position = [target[0], 10, target[2]];
|
||||||
|
|
||||||
@@ -159,23 +194,29 @@ const ZoneGroup: React.FC = () => {
|
|||||||
points: zone.points,
|
points: zone.points,
|
||||||
viewPortCenter: target,
|
viewPortCenter: target,
|
||||||
viewPortposition: position,
|
viewPortposition: position,
|
||||||
layer: zone.layer
|
layer: zone.layer,
|
||||||
}
|
},
|
||||||
}
|
|
||||||
|
|
||||||
socket.emit('v2:zone:set', input);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateZoneToBackend = async (zone: { zoneId: string; zoneName: string; points: [number, number, number][]; layer: string }) => {
|
socket.emit("v2:zone:set", input);
|
||||||
|
};
|
||||||
|
|
||||||
const email = localStorage.getItem('email');
|
const updateZoneToBackend = async (zone: {
|
||||||
const userId = localStorage.getItem('userId');
|
zoneId: string;
|
||||||
const organization = (email!.split("@")[1]).split(".")[0];
|
zoneName: string;
|
||||||
|
points: [number, number, number][];
|
||||||
|
layer: string;
|
||||||
|
}) => {
|
||||||
|
const email = localStorage.getItem("email");
|
||||||
|
const userId = localStorage.getItem("userId");
|
||||||
|
const organization = email!.split("@")[1].split(".")[0];
|
||||||
|
|
||||||
const calculateCenter = (points: number[][]) => {
|
const calculateCenter = (points: number[][]) => {
|
||||||
if (!points || points.length === 0) return null;
|
if (!points || points.length === 0) return null;
|
||||||
|
|
||||||
let sumX = 0, sumY = 0, sumZ = 0;
|
let sumX = 0,
|
||||||
|
sumY = 0,
|
||||||
|
sumZ = 0;
|
||||||
const numPoints = points.length;
|
const numPoints = points.length;
|
||||||
|
|
||||||
points.forEach(([x, y, z]) => {
|
points.forEach(([x, y, z]) => {
|
||||||
@@ -184,10 +225,16 @@ const ZoneGroup: React.FC = () => {
|
|||||||
sumZ += z;
|
sumZ += z;
|
||||||
});
|
});
|
||||||
|
|
||||||
return [sumX / numPoints, sumY / numPoints, sumZ / numPoints] as [number, number, number];
|
return [sumX / numPoints, sumY / numPoints, sumZ / numPoints] as [
|
||||||
|
number,
|
||||||
|
number,
|
||||||
|
number
|
||||||
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
const target: [number, number, number] | null = calculateCenter(zone.points);
|
const target: [number, number, number] | null = calculateCenter(
|
||||||
|
zone.points
|
||||||
|
);
|
||||||
if (!target) return;
|
if (!target) return;
|
||||||
const position = [target[0], 10, target[2]];
|
const position = [target[0], 10, target[2]];
|
||||||
|
|
||||||
@@ -200,27 +247,25 @@ const ZoneGroup: React.FC = () => {
|
|||||||
points: zone.points,
|
points: zone.points,
|
||||||
viewPortCenter: target,
|
viewPortCenter: target,
|
||||||
viewPortposition: position,
|
viewPortposition: position,
|
||||||
layer: zone.layer
|
layer: zone.layer,
|
||||||
}
|
},
|
||||||
}
|
|
||||||
|
|
||||||
socket.emit('v2:zone:set', input);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
socket.emit("v2:zone:set", input);
|
||||||
|
};
|
||||||
|
|
||||||
const deleteZoneFromBackend = async (zoneId: string) => {
|
const deleteZoneFromBackend = async (zoneId: string) => {
|
||||||
|
const email = localStorage.getItem("email");
|
||||||
const email = localStorage.getItem('email');
|
const userId = localStorage.getItem("userId");
|
||||||
const userId = localStorage.getItem('userId');
|
const organization = email!.split("@")[1].split(".")[0];
|
||||||
const organization = (email!.split("@")[1]).split(".")[0];
|
|
||||||
|
|
||||||
const input = {
|
const input = {
|
||||||
userId: userId,
|
userId: userId,
|
||||||
organization: organization,
|
organization: organization,
|
||||||
zoneId: zoneId
|
zoneId: zoneId,
|
||||||
}
|
};
|
||||||
|
|
||||||
socket.emit('v2:zone:delete', input);
|
socket.emit("v2:zone:delete", input);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleDeleteZone = (zoneId: string) => {
|
const handleDeleteZone = (zoneId: string) => {
|
||||||
@@ -229,9 +274,17 @@ const ZoneGroup: React.FC = () => {
|
|||||||
|
|
||||||
const zoneIndex = zones.findIndex((zone: any) => zone.zoneId === zoneId);
|
const zoneIndex = zones.findIndex((zone: any) => zone.zoneId === zoneId);
|
||||||
if (zoneIndex !== -1) {
|
if (zoneIndex !== -1) {
|
||||||
const zonePointsToRemove = zonePoints.slice(zoneIndex * 4, zoneIndex * 4 + 4);
|
const zonePointsToRemove = zonePoints.slice(
|
||||||
zonePointsToRemove.forEach((point: any) => groupsRef.current.remove(point));
|
zoneIndex * 4,
|
||||||
const updatedzonePoints = zonePoints.filter((_: any, index: any) => index < zoneIndex * 4 || index >= zoneIndex * 4 + 4);
|
zoneIndex * 4 + 4
|
||||||
|
);
|
||||||
|
zonePointsToRemove.forEach((point: any) =>
|
||||||
|
groupsRef.current.remove(point)
|
||||||
|
);
|
||||||
|
const updatedzonePoints = zonePoints.filter(
|
||||||
|
(_: any, index: any) =>
|
||||||
|
index < zoneIndex * 4 || index >= zoneIndex * 4 + 4
|
||||||
|
);
|
||||||
setZonePoints(updatedzonePoints);
|
setZonePoints(updatedzonePoints);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -251,11 +304,16 @@ const ZoneGroup: React.FC = () => {
|
|||||||
drag = false;
|
drag = false;
|
||||||
|
|
||||||
raycaster.setFromCamera(pointer, camera);
|
raycaster.setFromCamera(pointer, camera);
|
||||||
const intersects = raycaster.intersectObjects(groupsRef.current.children, true);
|
const intersects = raycaster.intersectObjects(
|
||||||
|
groupsRef.current.children,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
if (intersects.length > 0 && movePoint) {
|
if (intersects.length > 0 && movePoint) {
|
||||||
const clickedObject = intersects[0].object;
|
const clickedObject = intersects[0].object;
|
||||||
const sphereIndex = zonePoints.findIndex((point: any) => point.equals(clickedObject.position));
|
const sphereIndex = zonePoints.findIndex((point: any) =>
|
||||||
|
point.equals(clickedObject.position)
|
||||||
|
);
|
||||||
if (sphereIndex !== -1) {
|
if (sphereIndex !== -1) {
|
||||||
(controls as any).enabled = false;
|
(controls as any).enabled = false;
|
||||||
setDraggedSphere(zonePoints[sphereIndex]);
|
setDraggedSphere(zonePoints[sphereIndex]);
|
||||||
@@ -297,7 +355,7 @@ const ZoneGroup: React.FC = () => {
|
|||||||
zoneId,
|
zoneId,
|
||||||
zoneName,
|
zoneName,
|
||||||
points: points,
|
points: points,
|
||||||
layer: activeLayer
|
layer: activeLayer,
|
||||||
};
|
};
|
||||||
|
|
||||||
const newZones = [...zones, newZone];
|
const newZones = [...zones, newZone];
|
||||||
@@ -319,14 +377,24 @@ const ZoneGroup: React.FC = () => {
|
|||||||
setStartPoint(null);
|
setStartPoint(null);
|
||||||
setEndPoint(null);
|
setEndPoint(null);
|
||||||
}
|
}
|
||||||
} else if (evt.button === 0 && !drag && !isDragging && deletePointOrLine) {
|
} else if (
|
||||||
|
evt.button === 0 &&
|
||||||
|
!drag &&
|
||||||
|
!isDragging &&
|
||||||
|
deletePointOrLine
|
||||||
|
) {
|
||||||
raycaster.setFromCamera(pointer, camera);
|
raycaster.setFromCamera(pointer, camera);
|
||||||
const intersects = raycaster.intersectObjects(groupsRef.current.children, true);
|
const intersects = raycaster.intersectObjects(
|
||||||
|
groupsRef.current.children,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
if (intersects.length > 0) {
|
if (intersects.length > 0) {
|
||||||
const clickedObject = intersects[0].object;
|
const clickedObject = intersects[0].object;
|
||||||
|
|
||||||
const sphereIndex = zonePoints.findIndex((point: any) => point.equals(clickedObject.position));
|
const sphereIndex = zonePoints.findIndex((point: any) =>
|
||||||
|
point.equals(clickedObject.position)
|
||||||
|
);
|
||||||
if (sphereIndex !== -1) {
|
if (sphereIndex !== -1) {
|
||||||
const zoneIndex = Math.floor(sphereIndex / 4);
|
const zoneIndex = Math.floor(sphereIndex / 4);
|
||||||
const zoneId = zones[zoneIndex].zoneId;
|
const zoneId = zones[zoneIndex].zoneId;
|
||||||
@@ -341,7 +409,9 @@ const ZoneGroup: React.FC = () => {
|
|||||||
setIsDragging(false);
|
setIsDragging(false);
|
||||||
setDraggedSphere(null);
|
setDraggedSphere(null);
|
||||||
|
|
||||||
const sphereIndex = zonePoints.findIndex((point: any) => point === draggedSphere);
|
const sphereIndex = zonePoints.findIndex(
|
||||||
|
(point: any) => point === draggedSphere
|
||||||
|
);
|
||||||
if (sphereIndex !== -1) {
|
if (sphereIndex !== -1) {
|
||||||
const zoneIndex = Math.floor(sphereIndex / 4);
|
const zoneIndex = Math.floor(sphereIndex / 4);
|
||||||
|
|
||||||
@@ -358,9 +428,15 @@ const ZoneGroup: React.FC = () => {
|
|||||||
drag = true;
|
drag = true;
|
||||||
}
|
}
|
||||||
raycaster.setFromCamera(pointer, camera);
|
raycaster.setFromCamera(pointer, camera);
|
||||||
const intersects = raycaster.intersectObjects(groupsRef.current.children, true);
|
const intersects = raycaster.intersectObjects(
|
||||||
|
groupsRef.current.children,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
if (intersects.length > 0 && intersects[0].object.name.includes('point')) {
|
if (
|
||||||
|
intersects.length > 0 &&
|
||||||
|
intersects[0].object.name.includes("point")
|
||||||
|
) {
|
||||||
gl.domElement.style.cursor = movePoint ? "pointer" : "default";
|
gl.domElement.style.cursor = movePoint ? "pointer" : "default";
|
||||||
} else {
|
} else {
|
||||||
gl.domElement.style.cursor = "default";
|
gl.domElement.style.cursor = "default";
|
||||||
@@ -372,7 +448,9 @@ const ZoneGroup: React.FC = () => {
|
|||||||
if (point) {
|
if (point) {
|
||||||
draggedSphere.set(point.x, 0.15, point.z);
|
draggedSphere.set(point.x, 0.15, point.z);
|
||||||
|
|
||||||
const sphereIndex = zonePoints.findIndex((point: any) => point === draggedSphere);
|
const sphereIndex = zonePoints.findIndex(
|
||||||
|
(point: any) => point === draggedSphere
|
||||||
|
);
|
||||||
if (sphereIndex !== -1) {
|
if (sphereIndex !== -1) {
|
||||||
const zoneIndex = Math.floor(sphereIndex / 4);
|
const zoneIndex = Math.floor(sphereIndex / 4);
|
||||||
const cornerIndex = sphereIndex % 4;
|
const cornerIndex = sphereIndex % 4;
|
||||||
@@ -399,7 +477,7 @@ const ZoneGroup: React.FC = () => {
|
|||||||
setEndPoint(null);
|
setEndPoint(null);
|
||||||
};
|
};
|
||||||
|
|
||||||
if (toolMode === 'Zone' || deletePointOrLine || movePoint) {
|
if (toolMode === "Zone" || deletePointOrLine || movePoint) {
|
||||||
canvasElement.addEventListener("mousedown", onMouseDown);
|
canvasElement.addEventListener("mousedown", onMouseDown);
|
||||||
canvasElement.addEventListener("mouseup", onMouseUp);
|
canvasElement.addEventListener("mouseup", onMouseUp);
|
||||||
canvasElement.addEventListener("mousemove", onMouseMove);
|
canvasElement.addEventListener("mousemove", onMouseMove);
|
||||||
@@ -411,7 +489,21 @@ const ZoneGroup: React.FC = () => {
|
|||||||
canvasElement.removeEventListener("mousemove", onMouseMove);
|
canvasElement.removeEventListener("mousemove", onMouseMove);
|
||||||
canvasElement.removeEventListener("contextmenu", onContext);
|
canvasElement.removeEventListener("contextmenu", onContext);
|
||||||
};
|
};
|
||||||
}, [gl, camera, startPoint, toggleView, scene, toolMode, zones, isDragging, deletePointOrLine, zonePoints, draggedSphere, movePoint, activeLayer]);
|
}, [
|
||||||
|
gl,
|
||||||
|
camera,
|
||||||
|
startPoint,
|
||||||
|
toggleView,
|
||||||
|
scene,
|
||||||
|
toolMode,
|
||||||
|
zones,
|
||||||
|
isDragging,
|
||||||
|
deletePointOrLine,
|
||||||
|
zonePoints,
|
||||||
|
draggedSphere,
|
||||||
|
movePoint,
|
||||||
|
activeLayer,
|
||||||
|
]);
|
||||||
|
|
||||||
useFrame(() => {
|
useFrame(() => {
|
||||||
if (!startPoint) return;
|
if (!startPoint) return;
|
||||||
@@ -423,23 +515,36 @@ const ZoneGroup: React.FC = () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
return (
|
return (
|
||||||
<group ref={groupsRef} name='zoneGroup' >
|
<group ref={groupsRef} name="zoneGroup">
|
||||||
<group name="zones" visible={!toggleView}>
|
<group name="zones" visible={!toggleView}>
|
||||||
{zones
|
{zones.map((zone: any) => (
|
||||||
.map((zone: any) => (
|
|
||||||
<group key={zone.zoneId} name={zone.zoneName}>
|
<group key={zone.zoneId} name={zone.zoneName}>
|
||||||
{zone.points.slice(0, -1).map((point: [number, number, number], index: number) => {
|
{zone.points
|
||||||
|
.slice(0, -1)
|
||||||
|
.map((point: [number, number, number], index: number) => {
|
||||||
const nextPoint = zone.points[index + 1];
|
const nextPoint = zone.points[index + 1];
|
||||||
|
|
||||||
const point1 = new THREE.Vector3(point[0], point[1], point[2]);
|
const point1 = new THREE.Vector3(point[0], point[1], point[2]);
|
||||||
const point2 = new THREE.Vector3(nextPoint[0], nextPoint[1], nextPoint[2]);
|
const point2 = new THREE.Vector3(
|
||||||
|
nextPoint[0],
|
||||||
|
nextPoint[1],
|
||||||
|
nextPoint[2]
|
||||||
|
);
|
||||||
|
|
||||||
const planeWidth = point1.distanceTo(point2);
|
const planeWidth = point1.distanceTo(point2);
|
||||||
const planeHeight = CONSTANTS.zoneConfig.height;
|
const planeHeight = CONSTANTS.zoneConfig.height;
|
||||||
|
|
||||||
const midpoint = new THREE.Vector3((point1.x + point2.x) / 2, (CONSTANTS.zoneConfig.height / 2) + ((zone.layer - 1) * CONSTANTS.zoneConfig.height), (point1.z + point2.z) / 2);
|
const midpoint = new THREE.Vector3(
|
||||||
|
(point1.x + point2.x) / 2,
|
||||||
|
CONSTANTS.zoneConfig.height / 2 +
|
||||||
|
(zone.layer - 1) * CONSTANTS.zoneConfig.height,
|
||||||
|
(point1.z + point2.z) / 2
|
||||||
|
);
|
||||||
|
|
||||||
const angle = Math.atan2(point2.z - point1.z, point2.x - point1.x);
|
const angle = Math.atan2(
|
||||||
|
point2.z - point1.z,
|
||||||
|
point2.x - point1.x
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<mesh
|
<mesh
|
||||||
@@ -458,7 +563,7 @@ const ZoneGroup: React.FC = () => {
|
|||||||
</group>
|
</group>
|
||||||
))}
|
))}
|
||||||
</group>
|
</group>
|
||||||
<group name='zoneLines' visible={toggleView}>
|
<group name="zoneLines" visible={toggleView}>
|
||||||
{zones
|
{zones
|
||||||
.filter((zone: any) => zone.layer === activeLayer)
|
.filter((zone: any) => zone.layer === activeLayer)
|
||||||
.map((zone: any) => (
|
.map((zone: any) => (
|
||||||
@@ -477,7 +582,9 @@ const ZoneGroup: React.FC = () => {
|
|||||||
))}
|
))}
|
||||||
</group>
|
</group>
|
||||||
<group name="zonePoints" visible={toggleView}>
|
<group name="zonePoints" visible={toggleView}>
|
||||||
{zones.filter((zone: any) => zone.layer === activeLayer).flatMap((zone: any) => (
|
{zones
|
||||||
|
.filter((zone: any) => zone.layer === activeLayer)
|
||||||
|
.flatMap((zone: any) =>
|
||||||
zone.points.slice(0, 4).map((point: any, pointIndex: number) => (
|
zone.points.slice(0, 4).map((point: any, pointIndex: number) => (
|
||||||
<Sphere
|
<Sphere
|
||||||
key={`${zone.zoneId}-point-${pointIndex}`}
|
key={`${zone.zoneId}-point-${pointIndex}`}
|
||||||
@@ -488,7 +595,7 @@ const ZoneGroup: React.FC = () => {
|
|||||||
<meshBasicMaterial color="red" />
|
<meshBasicMaterial color="red" />
|
||||||
</Sphere>
|
</Sphere>
|
||||||
))
|
))
|
||||||
))}
|
)}
|
||||||
</group>
|
</group>
|
||||||
<group name="tempGroup" visible={toggleView}>
|
<group name="tempGroup" visible={toggleView}>
|
||||||
{startPoint && endPoint && (
|
{startPoint && endPoint && (
|
||||||
|
|||||||
@@ -1,55 +1,20 @@
|
|||||||
|
import html2canvas from "html2canvas";
|
||||||
|
|
||||||
export const captureVisualization = async (): Promise<string | null> => {
|
export const captureVisualization = async (): Promise<string | null> => {
|
||||||
const container = document.getElementById("real-time-vis-canvas");
|
const container = document.getElementById("real-time-vis-canvas");
|
||||||
if (!container) return null;
|
if (!container) return null;
|
||||||
|
|
||||||
const canvas = document.createElement("canvas");
|
|
||||||
const ctx = canvas.getContext("2d");
|
|
||||||
if (!ctx) return null;
|
|
||||||
|
|
||||||
const rect = container.getBoundingClientRect();
|
|
||||||
canvas.width = rect.width;
|
|
||||||
canvas.height = rect.height;
|
|
||||||
|
|
||||||
// Draw background
|
|
||||||
ctx.fillStyle = getComputedStyle(container).backgroundColor;
|
|
||||||
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
||||||
|
|
||||||
// Capture all canvas elements
|
|
||||||
const canvases = container.querySelectorAll("canvas");
|
|
||||||
canvases.forEach((childCanvas) => {
|
|
||||||
const childRect = childCanvas.getBoundingClientRect();
|
|
||||||
const x = childRect.left - rect.left;
|
|
||||||
const y = childRect.top - rect.top;
|
|
||||||
ctx.drawImage(childCanvas, x, y, childRect.width, childRect.height);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Capture SVG elements
|
|
||||||
const svgs = container.querySelectorAll("svg");
|
|
||||||
for (const svg of Array.from(svgs)) {
|
|
||||||
const svgString = new XMLSerializer().serializeToString(svg);
|
|
||||||
const svgBlob = new Blob([svgString], { type: "image/svg+xml" });
|
|
||||||
const url = URL.createObjectURL(svgBlob);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const img = await new Promise<HTMLImageElement>((resolve, reject) => {
|
// Use html2canvas to capture the container
|
||||||
const image = new Image();
|
const canvas = await html2canvas(container, {
|
||||||
image.onload = () => resolve(image);
|
scale: 1, // Adjust scale for higher/lower resolution
|
||||||
image.onerror = reject;
|
|
||||||
image.src = url;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const svgRect = svg.getBoundingClientRect();
|
// Convert the canvas to a data URL (PNG format)
|
||||||
ctx.drawImage(
|
const dataUrl = canvas.toDataURL("image/png");
|
||||||
img,
|
return dataUrl;
|
||||||
svgRect.left - rect.left,
|
} catch (error) {
|
||||||
svgRect.top - rect.top,
|
console.error("Error capturing visualization:", error);
|
||||||
svgRect.width,
|
return null;
|
||||||
svgRect.height
|
|
||||||
);
|
|
||||||
} finally {
|
|
||||||
URL.revokeObjectURL(url);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return canvas.toDataURL("image/png");
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -32,7 +32,6 @@ export const handleSaveTemplate = async ({
|
|||||||
try {
|
try {
|
||||||
// Check if the selected zone has any widgets
|
// Check if the selected zone has any widgets
|
||||||
if (!selectedZone.panelOrder || selectedZone.panelOrder.length === 0) {
|
if (!selectedZone.panelOrder || selectedZone.panelOrder.length === 0) {
|
||||||
console.warn("No widgets found in the selected zone.");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,7 +65,6 @@ export const handleSaveTemplate = async ({
|
|||||||
widgets3D,
|
widgets3D,
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log('newTemplate: ', newTemplate);
|
|
||||||
// Extract organization from email
|
// Extract organization from email
|
||||||
const email = localStorage.getItem("email") || "";
|
const email = localStorage.getItem("email") || "";
|
||||||
const organization = email.includes("@")
|
const organization = email.includes("@")
|
||||||
@@ -74,7 +72,6 @@ export const handleSaveTemplate = async ({
|
|||||||
: "";
|
: "";
|
||||||
|
|
||||||
if (!organization) {
|
if (!organization) {
|
||||||
console.error("Organization could not be determined from email.");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let saveTemplate = {
|
let saveTemplate = {
|
||||||
@@ -89,13 +86,13 @@ export const handleSaveTemplate = async ({
|
|||||||
try {
|
try {
|
||||||
addTemplate(newTemplate);
|
addTemplate(newTemplate);
|
||||||
// const response = await saveTemplateApi(organization, newTemplate);
|
// const response = await saveTemplateApi(organization, newTemplate);
|
||||||
// console.log("Save API Response:", response);
|
//
|
||||||
|
|
||||||
// Add template only if API call succeeds
|
// Add template only if API call succeeds
|
||||||
} catch (apiError) {
|
} catch (apiError) {
|
||||||
// console.error("Error saving template to API:", apiError);
|
//
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// console.error("Error in handleSaveTemplate:", error);
|
//
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -81,9 +81,12 @@
|
|||||||
|
|
||||||
.returnOfInvestment {
|
.returnOfInvestment {
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
|
min-width: 150px;
|
||||||
|
|
||||||
.charts {
|
.charts {
|
||||||
|
width: 100%;
|
||||||
height: 200px;
|
height: 200px;
|
||||||
|
min-width: 150px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.returns-wrapper {
|
.returns-wrapper {
|
||||||
@@ -126,6 +129,12 @@
|
|||||||
gap: 6px;
|
gap: 6px;
|
||||||
border-radius: 5.2px;
|
border-radius: 5.2px;
|
||||||
|
|
||||||
|
width: 100%;
|
||||||
|
height: 150px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
.header {
|
.header {
|
||||||
font-size: $small;
|
font-size: $small;
|
||||||
text-align: start;
|
text-align: start;
|
||||||
|
|||||||
@@ -75,6 +75,7 @@
|
|||||||
max-width: 80%;
|
max-width: 80%;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
max-width: calc(100% - 500px);
|
max-width: calc(100% - 500px);
|
||||||
|
min-width: 150px;
|
||||||
z-index: 3;
|
z-index: 3;
|
||||||
transform: translate(-50%, -10%);
|
transform: translate(-50%, -10%);
|
||||||
|
|
||||||
@@ -363,6 +364,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.panel.hidePanel {
|
.panel.hidePanel {
|
||||||
|
pointer-events: none;
|
||||||
opacity: 0.1;
|
opacity: 0.1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user