2025-03-25 06:17:41 +00:00
|
|
|
import React, { useEffect, useState, useRef } from "react";
|
2025-04-11 12:38:47 +00:00
|
|
|
import { usePlayButtonStore } from "../../store/usePlayButtonStore";
|
|
|
|
import Panel from "./widgets/panel/Panel";
|
|
|
|
import AddButtons from "./widgets/panel/AddButtons";
|
|
|
|
import { useSelectedZoneStore } from "../../store/useZoneStore";
|
2025-04-21 06:23:42 +00:00
|
|
|
import DisplayZone from "./zone/DisplayZone";
|
2025-04-11 12:38:47 +00:00
|
|
|
import Scene from "../scene/scene";
|
|
|
|
import useModuleStore from "../../store/useModuleStore";
|
2025-03-27 12:34:16 +00:00
|
|
|
|
2025-04-11 05:54:23 +00:00
|
|
|
import {
|
|
|
|
useDroppedObjectsStore,
|
|
|
|
useFloatingWidget,
|
2025-04-11 12:38:47 +00:00
|
|
|
} from "../../store/useDroppedObjectsStore";
|
2025-03-29 14:06:56 +00:00
|
|
|
import {
|
|
|
|
useAsset3dWidget,
|
2025-04-01 14:05:11 +00:00
|
|
|
useSocketStore,
|
2025-03-29 14:06:56 +00:00
|
|
|
useWidgetSubOption,
|
|
|
|
useZones,
|
2025-04-11 12:38:47 +00:00
|
|
|
} from "../../store/store";
|
2025-04-21 06:23:42 +00:00
|
|
|
import { getZone2dData } from "../../services/visulization/zone/getZoneData";
|
2025-04-11 12:38:47 +00:00
|
|
|
import { generateUniqueId } from "../../functions/generateUniqueId";
|
2025-03-29 13:51:20 +00:00
|
|
|
import { determinePosition } from "./functions/determinePosition";
|
2025-04-21 06:23:42 +00:00
|
|
|
import { addingFloatingWidgets } from "../../services/visulization/zone/addFloatingWidgets";
|
2025-04-11 12:38:47 +00:00
|
|
|
import SocketRealTimeViz from "./socket/realTimeVizSocket.dev";
|
|
|
|
import RenderOverlay from "../../components/templates/Overlay";
|
|
|
|
import ConfirmationPopup from "../../components/layout/confirmationPopup/ConfirmationPopup";
|
|
|
|
import DroppedObjects from "./widgets/floating/DroppedFloatingWidgets";
|
|
|
|
import EditWidgetOption from "../../components/ui/menu/EditWidgetOption";
|
2025-04-07 12:25:14 +00:00
|
|
|
import {
|
|
|
|
useEditWidgetOptionsStore,
|
|
|
|
useRightClickSelected,
|
|
|
|
useRightSelected,
|
2025-04-11 12:38:47 +00:00
|
|
|
} from "../../store/useZone3DWidgetStore";
|
|
|
|
import OuterClick from "../../utils/outerClick";
|
|
|
|
import { useWidgetStore } from "../../store/useWidgetStore";
|
2025-04-14 12:39:36 +00:00
|
|
|
import { getActiveProperties } from "./functions/getActiveProperties";
|
2025-03-26 13:00:33 +00:00
|
|
|
|
2025-03-25 06:17:41 +00:00
|
|
|
type Side = "top" | "bottom" | "left" | "right";
|
|
|
|
|
2025-03-26 06:58:22 +00:00
|
|
|
type FormattedZoneData = Record<
|
|
|
|
string,
|
|
|
|
{
|
|
|
|
activeSides: Side[];
|
|
|
|
panelOrder: Side[];
|
2025-04-10 12:23:28 +00:00
|
|
|
points: [];
|
2025-03-26 06:58:22 +00:00
|
|
|
lockedPanels: Side[];
|
2025-03-26 13:00:33 +00:00
|
|
|
zoneId: string;
|
|
|
|
zoneViewPortTarget: number[];
|
2025-03-29 13:32:02 +00:00
|
|
|
zoneViewPortPosition: number[];
|
2025-03-26 06:58:22 +00:00
|
|
|
widgets: Widget[];
|
|
|
|
}
|
|
|
|
>;
|
|
|
|
type Widget = {
|
2025-03-25 06:17:41 +00:00
|
|
|
id: string;
|
|
|
|
type: string;
|
|
|
|
title: string;
|
|
|
|
panel: Side;
|
|
|
|
data: any;
|
2025-03-26 06:58:22 +00:00
|
|
|
};
|
2025-03-25 06:17:41 +00:00
|
|
|
|
2025-04-09 12:36:08 +00:00
|
|
|
// Define the type for HiddenPanels, where keys are zone IDs and values are arrays of hidden sides
|
|
|
|
interface HiddenPanels {
|
|
|
|
[zoneId: string]: Side[];
|
|
|
|
}
|
|
|
|
|
2025-03-25 06:17:41 +00:00
|
|
|
const RealTimeVisulization: React.FC = () => {
|
2025-04-09 12:36:08 +00:00
|
|
|
const [hiddenPanels, setHiddenPanels] = React.useState<HiddenPanels>({});
|
2025-03-25 06:17:41 +00:00
|
|
|
const containerRef = useRef<HTMLDivElement>(null);
|
|
|
|
const { isPlaying } = usePlayButtonStore();
|
2025-03-26 07:02:04 +00:00
|
|
|
const { activeModule } = useModuleStore();
|
2025-03-27 06:58:17 +00:00
|
|
|
const [droppedObjects, setDroppedObjects] = useState<any[]>([]);
|
2025-03-26 06:58:22 +00:00
|
|
|
const [zonesData, setZonesData] = useState<FormattedZoneData>({});
|
2025-03-25 06:17:41 +00:00
|
|
|
const { selectedZone, setSelectedZone } = useSelectedZoneStore();
|
2025-04-04 13:17:47 +00:00
|
|
|
|
2025-04-07 12:25:14 +00:00
|
|
|
const { rightSelect, setRightSelect } = useRightSelected();
|
|
|
|
const { editWidgetOptions, setEditWidgetOptions } =
|
|
|
|
useEditWidgetOptionsStore();
|
|
|
|
const { rightClickSelected, setRightClickSelected } = useRightClickSelected();
|
2025-04-02 12:21:44 +00:00
|
|
|
const [openConfirmationPopup, setOpenConfirmationPopup] = useState(false);
|
|
|
|
|
2025-04-09 13:06:25 +00:00
|
|
|
// const [floatingWidgets, setFloatingWidgets] = useState<Record<string, { zoneName: string; zoneId: string; objects: any[] }>>({});
|
2025-04-11 05:54:23 +00:00
|
|
|
const { floatingWidget, setFloatingWidget } = useFloatingWidget();
|
2025-03-28 13:43:20 +00:00
|
|
|
const { widgetSelect, setWidgetSelect } = useAsset3dWidget();
|
2025-03-29 14:06:56 +00:00
|
|
|
const { widgetSubOption, setWidgetSubOption } = useWidgetSubOption();
|
2025-04-01 14:05:11 +00:00
|
|
|
const { visualizationSocket } = useSocketStore();
|
2025-04-11 05:54:23 +00:00
|
|
|
const { setSelectedChartId } = useWidgetStore();
|
|
|
|
|
|
|
|
OuterClick({
|
|
|
|
contextClassName: [
|
|
|
|
"chart-container",
|
|
|
|
"floating",
|
|
|
|
"sidebar-right-wrapper",
|
|
|
|
"card",
|
|
|
|
"dropdown-menu",
|
2025-04-14 12:44:40 +00:00
|
|
|
"dropdown-options",
|
2025-04-11 05:54:23 +00:00
|
|
|
],
|
|
|
|
setMenuVisible: () => setSelectedChartId(null),
|
|
|
|
});
|
2025-04-04 13:17:47 +00:00
|
|
|
|
2025-03-25 06:17:41 +00:00
|
|
|
useEffect(() => {
|
2025-03-28 13:43:20 +00:00
|
|
|
async function GetZoneData() {
|
|
|
|
const email = localStorage.getItem("email") || "";
|
|
|
|
const organization = email?.split("@")[1]?.split(".")[0];
|
|
|
|
try {
|
|
|
|
const response = await getZone2dData(organization);
|
2025-04-11 12:22:07 +00:00
|
|
|
// console.log('response: ', response);
|
2025-03-31 13:50:03 +00:00
|
|
|
|
2025-03-28 13:43:20 +00:00
|
|
|
if (!Array.isArray(response)) {
|
|
|
|
return;
|
|
|
|
}
|
2025-03-29 13:32:02 +00:00
|
|
|
const formattedData = response.reduce<FormattedZoneData>(
|
|
|
|
(acc, zone) => {
|
|
|
|
acc[zone.zoneName] = {
|
|
|
|
activeSides: [],
|
|
|
|
panelOrder: [],
|
|
|
|
lockedPanels: [],
|
2025-04-10 12:23:28 +00:00
|
|
|
points: zone.points,
|
2025-03-29 13:32:02 +00:00
|
|
|
zoneId: zone.zoneId,
|
|
|
|
zoneViewPortTarget: zone.viewPortCenter,
|
|
|
|
zoneViewPortPosition: zone.viewPortposition,
|
|
|
|
widgets: [],
|
|
|
|
};
|
|
|
|
return acc;
|
|
|
|
},
|
|
|
|
{}
|
|
|
|
);
|
2025-03-28 13:43:20 +00:00
|
|
|
setZonesData(formattedData);
|
2025-04-11 05:54:23 +00:00
|
|
|
} catch (error) {}
|
2025-03-28 13:43:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
GetZoneData();
|
2025-03-29 12:44:29 +00:00
|
|
|
}, [activeModule]); // Removed `zones` from dependencies
|
2025-03-26 06:58:22 +00:00
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
setZonesData((prev) => {
|
|
|
|
if (!selectedZone) return prev;
|
|
|
|
return {
|
|
|
|
...prev,
|
|
|
|
[selectedZone.zoneName]: {
|
|
|
|
...prev[selectedZone.zoneName], // Keep existing properties
|
|
|
|
activeSides: selectedZone.activeSides || [],
|
|
|
|
panelOrder: selectedZone.panelOrder || [],
|
|
|
|
lockedPanels: selectedZone.lockedPanels || [],
|
2025-04-11 05:54:23 +00:00
|
|
|
points: selectedZone.points || [],
|
2025-03-26 13:00:33 +00:00
|
|
|
zoneId: selectedZone.zoneId || "",
|
|
|
|
zoneViewPortTarget: selectedZone.zoneViewPortTarget || [],
|
|
|
|
zoneViewPortPosition: selectedZone.zoneViewPortPosition || [],
|
2025-03-26 06:58:22 +00:00
|
|
|
widgets: selectedZone.widgets || [],
|
|
|
|
},
|
|
|
|
};
|
|
|
|
});
|
2025-03-25 06:17:41 +00:00
|
|
|
}, [selectedZone]);
|
2025-03-28 13:43:20 +00:00
|
|
|
|
2025-03-31 13:50:03 +00:00
|
|
|
// useEffect(() => {}, [floatingWidgets]);
|
2025-03-25 06:17:41 +00:00
|
|
|
|
2025-03-29 13:51:20 +00:00
|
|
|
const handleDrop = async (event: React.DragEvent<HTMLDivElement>) => {
|
2025-04-08 12:44:59 +00:00
|
|
|
event.preventDefault();
|
2025-03-29 13:51:20 +00:00
|
|
|
try {
|
|
|
|
const email = localStorage.getItem("email") || "";
|
|
|
|
const organization = email?.split("@")[1]?.split(".")[0];
|
2025-04-14 12:44:40 +00:00
|
|
|
|
2025-03-29 13:51:20 +00:00
|
|
|
const data = event.dataTransfer.getData("text/plain");
|
2025-03-29 14:06:56 +00:00
|
|
|
if (widgetSubOption === "3D") return;
|
2025-03-29 13:51:20 +00:00
|
|
|
if (!data || selectedZone.zoneName === "") return;
|
2025-04-14 12:44:40 +00:00
|
|
|
|
2025-03-29 13:51:20 +00:00
|
|
|
const droppedData = JSON.parse(data);
|
|
|
|
const canvasElement = document.getElementById("real-time-vis-canvas");
|
|
|
|
if (!canvasElement) throw new Error("Canvas element not found");
|
2025-04-14 12:44:40 +00:00
|
|
|
|
2025-04-11 12:38:47 +00:00
|
|
|
const rect = canvasElement.getBoundingClientRect();
|
2025-04-14 12:39:36 +00:00
|
|
|
const relativeX = event.clientX - rect.left;
|
|
|
|
const relativeY = event.clientY - rect.top;
|
2025-04-14 12:44:40 +00:00
|
|
|
|
2025-04-14 12:39:36 +00:00
|
|
|
// Widget dimensions
|
|
|
|
const widgetWidth = droppedData.width || 125;
|
|
|
|
const widgetHeight = droppedData.height || 100;
|
2025-04-14 12:44:40 +00:00
|
|
|
|
2025-04-14 12:39:36 +00:00
|
|
|
// Center the widget at cursor
|
|
|
|
const centerOffsetX = widgetWidth / 2;
|
|
|
|
const centerOffsetY = widgetHeight / 2;
|
2025-04-14 12:44:40 +00:00
|
|
|
|
2025-04-14 12:39:36 +00:00
|
|
|
const adjustedX = relativeX - centerOffsetX;
|
|
|
|
const adjustedY = relativeY - centerOffsetY;
|
2025-04-14 12:44:40 +00:00
|
|
|
|
2025-04-14 12:39:36 +00:00
|
|
|
const finalPosition = determinePosition(rect, adjustedX, adjustedY);
|
|
|
|
const [activeProp1, activeProp2] = getActiveProperties(finalPosition);
|
2025-04-14 12:44:40 +00:00
|
|
|
|
2025-04-14 12:39:36 +00:00
|
|
|
let finalY = 0;
|
|
|
|
let finalX = 0;
|
2025-04-14 12:44:40 +00:00
|
|
|
|
2025-04-14 12:39:36 +00:00
|
|
|
if (activeProp1 === "top") {
|
|
|
|
finalY = adjustedY;
|
|
|
|
} else {
|
|
|
|
finalY = rect.height - (adjustedY + widgetHeight);
|
|
|
|
}
|
2025-04-14 12:44:40 +00:00
|
|
|
|
2025-04-14 12:39:36 +00:00
|
|
|
if (activeProp2 === "left") {
|
|
|
|
finalX = adjustedX;
|
|
|
|
} else {
|
|
|
|
finalX = rect.width - (adjustedX + widgetWidth);
|
|
|
|
}
|
2025-04-14 12:44:40 +00:00
|
|
|
|
2025-04-14 12:39:36 +00:00
|
|
|
// Clamp to boundaries
|
|
|
|
finalX = Math.max(0, Math.min(rect.width - widgetWidth, finalX));
|
|
|
|
finalY = Math.max(0, Math.min(rect.height - widgetHeight, finalY));
|
2025-04-14 12:44:40 +00:00
|
|
|
|
2025-04-14 12:39:36 +00:00
|
|
|
const boundedPosition = {
|
|
|
|
...finalPosition,
|
|
|
|
[activeProp1]: finalY,
|
|
|
|
[activeProp2]: finalX,
|
|
|
|
[activeProp1 === "top" ? "bottom" : "top"]: "auto",
|
|
|
|
[activeProp2 === "left" ? "right" : "left"]: "auto",
|
|
|
|
};
|
2025-04-14 12:44:40 +00:00
|
|
|
|
2025-03-29 13:51:20 +00:00
|
|
|
const newObject = {
|
|
|
|
...droppedData,
|
|
|
|
id: generateUniqueId(),
|
2025-04-14 12:39:36 +00:00
|
|
|
position: boundedPosition,
|
2025-03-29 13:51:20 +00:00
|
|
|
};
|
2025-04-14 12:44:40 +00:00
|
|
|
|
2025-04-14 12:39:36 +00:00
|
|
|
const existingZone =
|
|
|
|
useDroppedObjectsStore.getState().zones[selectedZone.zoneName];
|
2025-03-29 13:51:20 +00:00
|
|
|
if (!existingZone) {
|
2025-04-14 12:39:36 +00:00
|
|
|
useDroppedObjectsStore
|
|
|
|
.getState()
|
|
|
|
.setZone(selectedZone.zoneName, selectedZone.zoneId);
|
2025-03-29 13:51:20 +00:00
|
|
|
}
|
2025-04-14 12:44:40 +00:00
|
|
|
|
2025-04-11 12:38:47 +00:00
|
|
|
const addFloatingWidget = {
|
|
|
|
organization,
|
2025-04-02 13:19:18 +00:00
|
|
|
widget: newObject,
|
2025-04-04 12:17:15 +00:00
|
|
|
zoneId: selectedZone.zoneId,
|
|
|
|
};
|
2025-04-14 12:44:40 +00:00
|
|
|
|
2025-04-02 13:19:18 +00:00
|
|
|
if (visualizationSocket) {
|
2025-04-04 12:17:15 +00:00
|
|
|
visualizationSocket.emit("v2:viz-float:add", addFloatingWidget);
|
2025-04-02 13:19:18 +00:00
|
|
|
}
|
2025-04-14 12:44:40 +00:00
|
|
|
|
2025-04-14 12:39:36 +00:00
|
|
|
useDroppedObjectsStore
|
|
|
|
.getState()
|
|
|
|
.addObject(selectedZone.zoneName, newObject);
|
2025-04-14 12:44:40 +00:00
|
|
|
|
2025-04-09 13:06:25 +00:00
|
|
|
const droppedObjectsStore = useDroppedObjectsStore.getState();
|
|
|
|
const currentZone = droppedObjectsStore.zones[selectedZone.zoneName];
|
2025-04-14 12:44:40 +00:00
|
|
|
|
2025-04-09 13:06:25 +00:00
|
|
|
if (currentZone && currentZone.zoneId === selectedZone.zoneId) {
|
2025-04-14 12:39:36 +00:00
|
|
|
console.log(
|
|
|
|
`Objects for Zone ${selectedZone.zoneId}:`,
|
|
|
|
currentZone.objects
|
|
|
|
);
|
2025-04-11 05:54:23 +00:00
|
|
|
setFloatingWidget(currentZone.objects);
|
2025-04-09 13:06:25 +00:00
|
|
|
} else {
|
2025-04-11 12:38:47 +00:00
|
|
|
console.warn("Zone not found or zoneId mismatch");
|
2025-04-09 13:06:25 +00:00
|
|
|
}
|
2025-04-11 12:38:47 +00:00
|
|
|
} catch (error) {
|
|
|
|
console.error("Error in handleDrop:", error);
|
|
|
|
}
|
2025-03-26 13:00:33 +00:00
|
|
|
};
|
2025-03-28 13:43:20 +00:00
|
|
|
|
2025-04-04 12:17:15 +00:00
|
|
|
useEffect(() => {
|
|
|
|
const handleClickOutside = (event: MouseEvent) => {
|
|
|
|
const editWidgetOptions = document.querySelector(
|
|
|
|
".editWidgetOptions-wrapper"
|
|
|
|
);
|
|
|
|
if (
|
|
|
|
editWidgetOptions &&
|
|
|
|
!editWidgetOptions.contains(event.target as Node)
|
|
|
|
) {
|
|
|
|
setRightClickSelected(null);
|
|
|
|
setRightSelect(null);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
document.addEventListener("mousedown", handleClickOutside);
|
|
|
|
return () => {
|
|
|
|
document.removeEventListener("mousedown", handleClickOutside);
|
|
|
|
};
|
|
|
|
}, [setRightClickSelected]);
|
|
|
|
|
2025-04-14 12:39:36 +00:00
|
|
|
const [canvasDimensions, setCanvasDimensions] = useState({
|
|
|
|
width: 0,
|
|
|
|
height: 0,
|
|
|
|
});
|
|
|
|
useEffect(() => {
|
|
|
|
const canvas = document.getElementById("real-time-vis-canvas");
|
|
|
|
if (!canvas) return;
|
|
|
|
|
|
|
|
const updateCanvasDimensions = () => {
|
|
|
|
const rect = canvas.getBoundingClientRect();
|
|
|
|
setCanvasDimensions({
|
|
|
|
width: rect.width,
|
|
|
|
height: rect.height,
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
updateCanvasDimensions();
|
|
|
|
const resizeObserver = new ResizeObserver(updateCanvasDimensions);
|
|
|
|
resizeObserver.observe(canvas);
|
|
|
|
|
|
|
|
return () => resizeObserver.unobserve(canvas);
|
|
|
|
}, []);
|
|
|
|
|
2025-03-25 06:17:41 +00:00
|
|
|
return (
|
2025-04-07 12:25:14 +00:00
|
|
|
<>
|
2025-04-14 12:44:40 +00:00
|
|
|
<style>
|
2025-04-14 12:39:36 +00:00
|
|
|
{`
|
|
|
|
:root {
|
|
|
|
--realTimeViz-container-width: ${canvasDimensions.width}px;
|
|
|
|
|
|
|
|
--realTimeViz-container-height: ${canvasDimensions.height}px;
|
|
|
|
|
|
|
|
}
|
|
|
|
`}
|
|
|
|
</style>
|
2025-03-25 11:00:33 +00:00
|
|
|
<div
|
2025-04-07 12:25:14 +00:00
|
|
|
ref={containerRef}
|
|
|
|
id="real-time-vis-canvas"
|
|
|
|
className={`realTime-viz canvas ${isPlaying ? "playingFlase" : ""}`}
|
|
|
|
style={{
|
|
|
|
height: isPlaying || activeModule !== "visualization" ? "100vh" : "",
|
|
|
|
width: isPlaying || activeModule !== "visualization" ? "100vw" : "",
|
|
|
|
left: isPlaying || activeModule !== "visualization" ? "0%" : "",
|
|
|
|
}}
|
|
|
|
>
|
|
|
|
<div className="realTime-viz-wrapper">
|
|
|
|
{openConfirmationPopup && (
|
|
|
|
<RenderOverlay>
|
|
|
|
<ConfirmationPopup
|
|
|
|
message={"Are you sure want to delete?"}
|
2025-04-11 12:38:47 +00:00
|
|
|
onConfirm={() => console.log("Confirmed")}
|
2025-04-07 12:25:14 +00:00
|
|
|
onCancel={() => setOpenConfirmationPopup(false)}
|
|
|
|
/>
|
|
|
|
</RenderOverlay>
|
|
|
|
)}
|
2025-04-07 12:58:52 +00:00
|
|
|
<div
|
|
|
|
className="scene-container"
|
|
|
|
style={{
|
|
|
|
height: "100%",
|
|
|
|
width: "100%",
|
|
|
|
borderRadius:
|
|
|
|
isPlaying || activeModule !== "visualization" ? "" : "6px",
|
|
|
|
}}
|
|
|
|
onDrop={(event) => handleDrop(event)}
|
|
|
|
onDragOver={(event) => event.preventDefault()}
|
2025-04-07 12:25:14 +00:00
|
|
|
>
|
2025-04-07 12:58:52 +00:00
|
|
|
<Scene />
|
|
|
|
</div>
|
2025-04-07 12:25:14 +00:00
|
|
|
{activeModule === "visualization" && selectedZone.zoneName !== "" && (
|
|
|
|
<DroppedObjects />
|
2025-03-26 07:02:04 +00:00
|
|
|
)}
|
2025-04-07 12:25:14 +00:00
|
|
|
{activeModule === "visualization" && <SocketRealTimeViz />}
|
|
|
|
|
|
|
|
{activeModule === "visualization" &&
|
|
|
|
editWidgetOptions &&
|
|
|
|
rightClickSelected && (
|
|
|
|
<EditWidgetOption
|
|
|
|
options={[
|
|
|
|
"Duplicate",
|
|
|
|
"Vertical Move",
|
|
|
|
"Horizontal Move",
|
|
|
|
"RotateX",
|
|
|
|
"RotateY",
|
|
|
|
"Delete",
|
|
|
|
]}
|
|
|
|
/>
|
|
|
|
)}
|
|
|
|
|
|
|
|
{activeModule === "visualization" && (
|
|
|
|
<>
|
|
|
|
<DisplayZone
|
|
|
|
zonesData={zonesData}
|
|
|
|
selectedZone={selectedZone}
|
|
|
|
setSelectedZone={setSelectedZone}
|
2025-04-10 12:43:41 +00:00
|
|
|
hiddenPanels={hiddenPanels}
|
|
|
|
setHiddenPanels={setHiddenPanels}
|
2025-04-07 12:25:14 +00:00
|
|
|
/>
|
2025-03-26 07:02:04 +00:00
|
|
|
|
2025-04-07 12:25:14 +00:00
|
|
|
{!isPlaying && selectedZone?.zoneName !== "" && (
|
|
|
|
<AddButtons
|
|
|
|
hiddenPanels={hiddenPanels}
|
|
|
|
setHiddenPanels={setHiddenPanels}
|
|
|
|
selectedZone={selectedZone}
|
|
|
|
setSelectedZone={setSelectedZone}
|
|
|
|
/>
|
|
|
|
)}
|
|
|
|
|
|
|
|
<Panel
|
|
|
|
selectedZone={selectedZone}
|
|
|
|
setSelectedZone={setSelectedZone}
|
|
|
|
hiddenPanels={hiddenPanels}
|
|
|
|
setZonesData={setZonesData}
|
|
|
|
/>
|
|
|
|
</>
|
|
|
|
)}
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</>
|
2025-03-25 06:17:41 +00:00
|
|
|
);
|
|
|
|
};
|
|
|
|
|
|
|
|
export default RealTimeVisulization;
|