Dwinzo_dev/app/src/modules/visualization/RealTimeVisulization.tsx

402 lines
13 KiB
TypeScript
Raw Normal View History

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
import {
useDroppedObjectsStore,
useFloatingWidget,
2025-04-11 12:38:47 +00:00
} from "../../store/useDroppedObjectsStore";
import {
useAsset3dWidget,
2025-04-01 14:05:11 +00:00
useSocketStore,
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";
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-25 06:17:41 +00:00
type Side = "top" | "bottom" | "left" | "right";
type FormattedZoneData = Record<
string,
{
activeSides: Side[];
panelOrder: Side[];
points: [];
lockedPanels: Side[];
zoneId: string;
zoneViewPortTarget: number[];
zoneViewPortPosition: number[];
widgets: Widget[];
}
>;
type Widget = {
2025-03-25 06:17:41 +00:00
id: string;
type: string;
title: string;
panel: Side;
data: any;
};
2025-03-25 06:17:41 +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 = () => {
const [hiddenPanels, setHiddenPanels] = React.useState<HiddenPanels>({});
2025-03-25 06:17:41 +00:00
const containerRef = useRef<HTMLDivElement>(null);
const { isPlaying } = usePlayButtonStore();
const { activeModule } = useModuleStore();
2025-03-27 06:58:17 +00:00
const [droppedObjects, setDroppedObjects] = useState<any[]>([]);
const [zonesData, setZonesData] = useState<FormattedZoneData>({});
2025-03-25 06:17:41 +00:00
const { selectedZone, setSelectedZone } = useSelectedZoneStore();
2025-04-07 12:25:14 +00:00
const { rightSelect, setRightSelect } = useRightSelected();
const { editWidgetOptions, setEditWidgetOptions } =
useEditWidgetOptionsStore();
const { rightClickSelected, setRightClickSelected } = useRightClickSelected();
const [openConfirmationPopup, setOpenConfirmationPopup] = useState(false);
// const [floatingWidgets, setFloatingWidgets] = useState<Record<string, { zoneName: string; zoneId: string; objects: any[] }>>({});
const { floatingWidget, setFloatingWidget } = useFloatingWidget();
const { widgetSelect, setWidgetSelect } = useAsset3dWidget();
const { widgetSubOption, setWidgetSubOption } = useWidgetSubOption();
2025-04-01 14:05:11 +00:00
const { visualizationSocket } = useSocketStore();
const { setSelectedChartId } = useWidgetStore();
OuterClick({
contextClassName: [
"chart-container",
"floating",
"sidebar-right-wrapper",
"card",
"dropdown-menu",
2025-04-14 12:44:40 +00:00
"dropdown-options",
],
setMenuVisible: () => setSelectedChartId(null),
});
2025-03-25 06:17:41 +00:00
useEffect(() => {
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);
if (!Array.isArray(response)) {
return;
}
const formattedData = response.reduce<FormattedZoneData>(
(acc, zone) => {
acc[zone.zoneName] = {
activeSides: [],
panelOrder: [],
lockedPanels: [],
points: zone.points,
zoneId: zone.zoneId,
zoneViewPortTarget: zone.viewPortCenter,
zoneViewPortPosition: zone.viewPortposition,
widgets: [],
};
return acc;
},
{}
);
setZonesData(formattedData);
} catch (error) {}
}
GetZoneData();
}, [activeModule]); // Removed `zones` from dependencies
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 || [],
points: selectedZone.points || [],
zoneId: selectedZone.zoneId || "",
zoneViewPortTarget: selectedZone.zoneViewPortTarget || [],
zoneViewPortPosition: selectedZone.zoneViewPortPosition || [],
widgets: selectedZone.widgets || [],
},
};
});
2025-03-25 06:17:41 +00:00
}, [selectedZone]);
// useEffect(() => {}, [floatingWidgets]);
2025-03-25 06:17:41 +00:00
const handleDrop = async (event: React.DragEvent<HTMLDivElement>) => {
event.preventDefault();
try {
const email = localStorage.getItem("email") || "";
const organization = email?.split("@")[1]?.split(".")[0];
2025-04-14 12:44:40 +00:00
const data = event.dataTransfer.getData("text/plain");
if (widgetSubOption === "3D") return;
if (!data || selectedZone.zoneName === "") return;
2025-04-14 12:44:40 +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
const newObject = {
...droppedData,
id: generateUniqueId(),
2025-04-14 12:39:36 +00:00
position: boundedPosition,
};
2025-04-14 12:44:40 +00:00
2025-04-14 12:39:36 +00:00
const existingZone =
useDroppedObjectsStore.getState().zones[selectedZone.zoneName];
if (!existingZone) {
2025-04-14 12:39:36 +00:00
useDroppedObjectsStore
.getState()
.setZone(selectedZone.zoneName, selectedZone.zoneId);
}
2025-04-14 12:44:40 +00:00
2025-04-11 12:38:47 +00:00
const addFloatingWidget = {
organization,
widget: newObject,
2025-04-04 12:17:15 +00:00
zoneId: selectedZone.zoneId,
};
2025-04-14 12:44:40 +00:00
if (visualizationSocket) {
2025-04-04 12:17:15 +00:00
visualizationSocket.emit("v2:viz-float:add", addFloatingWidget);
}
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
const droppedObjectsStore = useDroppedObjectsStore.getState();
const currentZone = droppedObjectsStore.zones[selectedZone.zoneName];
2025-04-14 12:44:40 +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
);
setFloatingWidget(currentZone.objects);
} else {
2025-04-11 12:38:47 +00:00
console.warn("Zone not found or zoneId mismatch");
}
2025-04-11 12:38:47 +00:00
} catch (error) {
console.error("Error in handleDrop:", error);
}
};
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>
)}
<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
>
<Scene />
</div>
2025-04-07 12:25:14 +00:00
{activeModule === "visualization" && selectedZone.zoneName !== "" && (
<DroppedObjects />
)}
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}
hiddenPanels={hiddenPanels}
setHiddenPanels={setHiddenPanels}
2025-04-07 12:25:14 +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;