Merge remote-tracking branch 'origin/main' into realTimeVisulization

This commit is contained in:
gabriel 2025-04-02 18:11:35 +05:30
commit f175822c3c
18 changed files with 337 additions and 278 deletions

View File

@ -207,6 +207,7 @@ const ReturnOfInvestment: React.FC<ReturnOfInvestmentProps> = ({ id, type, posit
<Html position={[position[0], position[1], position[2]]} <Html position={[position[0], position[1], position[2]]}
scale={[0.5, 0.5, 0.5]} scale={[0.5, 0.5, 0.5]}
transform transform
zIndexRange={[1,0]}
sprite> sprite>
<div className="returnOfInvestment card" <div className="returnOfInvestment card"
onClick={ onClick={

View File

@ -93,6 +93,7 @@ const StateWorking: React.FC<StateWorkingProps> = ({ id, type, position }) => {
<Html position={[position[0], position[1], position[2]]} <Html position={[position[0], position[1], position[2]]}
scale={[0.5, 0.5, 0.5]} scale={[0.5, 0.5, 0.5]}
transform transform
zIndexRange={[1,0]}
sprite> sprite>
<div className="stateWorking-wrapper card" <div className="stateWorking-wrapper card"
onClick={ onClick={

View File

@ -187,6 +187,7 @@ const Throughput: React.FC<ThroughputProps> = ({ id, type, position }) => {
<Html position={[position[0], position[1], position[2]]} <Html position={[position[0], position[1], position[2]]}
scale={[0.5, 0.5, 0.5]} scale={[0.5, 0.5, 0.5]}
transform transform
zIndexRange={[1, 0]}
sprite> sprite>
<div className="throughput-wrapper" <div className="throughput-wrapper"
onClick={ onClick={

View File

@ -1,4 +1,4 @@
import React from "react"; import React, { useEffect } from "react";
import { import {
CleanPannel, CleanPannel,
EyeIcon, EyeIcon,
@ -6,6 +6,8 @@ import {
} from "../../icons/RealTimeVisulationIcons"; } from "../../icons/RealTimeVisulationIcons";
import { panelData } from "../../../services/realTimeVisulization/zoneData/panel"; import { panelData } from "../../../services/realTimeVisulization/zoneData/panel";
import { AddIcon } from "../../icons/ExportCommonIcons"; import { AddIcon } from "../../icons/ExportCommonIcons";
import { deletePanelApi } from "../../../services/realTimeVisulization/zoneData/deletePanel";
import { useSocketStore } from "../../../store/store";
// Define the type for `Side` // Define the type for `Side`
type Side = "top" | "bottom" | "left" | "right"; type Side = "top" | "bottom" | "left" | "right";
@ -16,7 +18,6 @@ interface ButtonsProps {
zoneName: string; zoneName: string;
activeSides: Side[]; activeSides: Side[];
panelOrder: Side[]; panelOrder: Side[];
lockedPanels: Side[]; lockedPanels: Side[];
zoneId: string; zoneId: string;
zoneViewPortTarget: number[]; zoneViewPortTarget: number[];
@ -58,6 +59,9 @@ const AddButtons: React.FC<ButtonsProps> = ({
setHiddenPanels, setHiddenPanels,
hiddenPanels, hiddenPanels,
}) => { }) => {
const { visualizationSocket } = useSocketStore();
// Local state to track hidden panels // Local state to track hidden panels
// Function to toggle lock/unlock a panel // Function to toggle lock/unlock a panel
@ -103,9 +107,13 @@ const AddButtons: React.FC<ButtonsProps> = ({
}; };
// Function to handle "+" button click // Function to handle "+" button click
const handlePlusButtonClick = (side: Side) => { const handlePlusButtonClick = async (side: Side) => {
if (selectedZone.activeSides.includes(side)) { if (selectedZone.activeSides.includes(side)) {
// If the panel is already active, remove all widgets and close the panel // Panel already exists: Remove widgets from that side and update activeSides
const email = localStorage.getItem("email") || "";
const organization = email?.split("@")[1]?.split(".")[0]; // Fallback value
// Remove all widgets associated with the side and update active sides
const cleanedWidgets = selectedZone.widgets.filter( const cleanedWidgets = selectedZone.widgets.filter(
(widget) => widget.panel !== side (widget) => widget.panel !== side
); );
@ -118,44 +126,68 @@ const AddButtons: React.FC<ButtonsProps> = ({
panelOrder: newActiveSides, panelOrder: newActiveSides,
}; };
// Delete the selectedZone state let deletePanel = {
organization: organization,
panelName: side,
zoneId: selectedZone.zoneId
}
if (visualizationSocket) {
visualizationSocket.emit("v2:viz-panel:delete", deletePanel)
}
setSelectedZone(updatedZone); setSelectedZone(updatedZone);
// API call to delete the panel
// try {
// const response = await deletePanelApi(selectedZone.zoneId, side, organization);
//
// if (response.message === "Panel deleted successfully") {
// } else {
//
// }
// } catch (error) {
//
// }
} else { } else {
const updatePanelData = async () => { // Panel does not exist: Create panel
try { try {
// Get email and organization safely // Get email and organization safely with a default fallback
const email = localStorage.getItem("email") || ""; const email = localStorage.getItem("email") || "";
const organization = email?.split("@")[1]?.split(".")[0] || "defaultOrg"; // Fallback value const organization = email?.split("@")[1]?.split(".")[0];
// Prevent duplicate side entries // Prevent duplicate side entries
const newActiveSides = selectedZone.activeSides.includes(side) const newActiveSides = selectedZone.activeSides.includes(side)
? [...selectedZone.activeSides] ? [...selectedZone.activeSides]
: [...selectedZone.activeSides, side]; : [...selectedZone.activeSides, side];
const updatedZone = {
...selectedZone,
activeSides: newActiveSides,
panelOrder: newActiveSides,
};
// API call
const response = await panelData(organization, selectedZone.zoneId, newActiveSides);
// Update state
setSelectedZone(updatedZone);
} catch (error) {
const updatedZone = {
...selectedZone,
activeSides: newActiveSides,
panelOrder: newActiveSides,
};
let addPanel = {
organization: organization,
zoneId: selectedZone.zoneId,
panelOrder: newActiveSides
} }
}; if (visualizationSocket) {
visualizationSocket.emit("v2:viz-panel:add", addPanel)
}
setSelectedZone(updatedZone);
// API call to create panels
// const response = await panelData(organization, selectedZone.zoneId, newActiveSides);
//
updatePanelData(); // Call the async function // if (response.message === "Panels created successfully") {
// } else {
//
// }
} catch (error) {
}
} }
}; };
return ( return (
<> <>
<div> <div>

View File

@ -2,7 +2,10 @@ import React, { useEffect, useRef, useState, useCallback } from "react";
import { useWidgetStore, Widget } from "../../../store/useWidgetStore"; import { useWidgetStore, Widget } from "../../../store/useWidgetStore";
import { MoveArrowLeft, MoveArrowRight } from "../../icons/SimulationIcons"; import { MoveArrowLeft, MoveArrowRight } from "../../icons/SimulationIcons";
import { InfoIcon } from "../../icons/ExportCommonIcons"; import { InfoIcon } from "../../icons/ExportCommonIcons";
import { useDroppedObjectsStore, useFloatingWidget } from "../../../store/useDroppedObjectsStore"; import {
useDroppedObjectsStore,
useFloatingWidget,
} from "../../../store/useDroppedObjectsStore";
import { getSelect2dZoneData } from "../../../services/realTimeVisulization/zoneData/getSelect2dZoneData"; import { getSelect2dZoneData } from "../../../services/realTimeVisulization/zoneData/getSelect2dZoneData";
import { getFloatingZoneData } from "../../../services/realTimeVisulization/zoneData/getFloatingData"; import { getFloatingZoneData } from "../../../services/realTimeVisulization/zoneData/getFloatingData";
import { get3dWidgetZoneData } from "../../../services/realTimeVisulization/zoneData/get3dWidgetData"; import { get3dWidgetZoneData } from "../../../services/realTimeVisulization/zoneData/get3dWidgetData";
@ -63,7 +66,7 @@ const DisplayZone: React.FC<DisplayZoneProps> = ({
selectedZone, selectedZone,
setSelectedZone, setSelectedZone,
}) => { }) => {
// Refs // Ref for the container element
const containerRef = useRef<HTMLDivElement | null>(null); const containerRef = useRef<HTMLDivElement | null>(null);
const scrollContainerRef = useRef<HTMLDivElement | null>(null); const scrollContainerRef = useRef<HTMLDivElement | null>(null);
@ -152,10 +155,11 @@ const DisplayZone: React.FC<DisplayZoneProps> = ({
const email = localStorage.getItem("email") || ""; const email = localStorage.getItem("email") || "";
const organization = email?.split("@")[1]?.split(".")[0]; const organization = email?.split("@")[1]?.split(".")[0];
let response = await getSelect2dZoneData(zoneId, organization); let response = await getSelect2dZoneData(zoneId, organization);
console.log('response: ', response);
let res = await getFloatingZoneData(zoneId, organization);
setFloatingWidget(res);
let res = await getFloatingZoneData(zoneId, organization);
setFloatingWidget(res);
// Set the selected zone in the store
useDroppedObjectsStore.getState().setZone(zoneName, zoneId); useDroppedObjectsStore.getState().setZone(zoneName, zoneId);
if (Array.isArray(res)) { if (Array.isArray(res)) {
res.forEach((val) => { res.forEach((val) => {

View File

@ -15,6 +15,7 @@ import {
import { useEffect, useRef, useState } from "react"; import { useEffect, useRef, useState } from "react";
import { duplicateWidgetApi } from "../../../services/realTimeVisulization/zoneData/duplicateWidget"; import { duplicateWidgetApi } from "../../../services/realTimeVisulization/zoneData/duplicateWidget";
import { deleteWidgetApi } from "../../../services/realTimeVisulization/zoneData/deleteWidgetApi"; import { deleteWidgetApi } from "../../../services/realTimeVisulization/zoneData/deleteWidgetApi";
import { useSocketStore } from "../../../store/store";
type Side = "top" | "bottom" | "left" | "right"; type Side = "top" | "bottom" | "left" | "right";
@ -71,6 +72,7 @@ export const DraggableWidget = ({
openKebabId: string | null; openKebabId: string | null;
setOpenKebabId: (id: string | null) => void; setOpenKebabId: (id: string | null) => void;
}) => { }) => {
const { visualizationSocket } = useSocketStore();
const { selectedChartId, setSelectedChartId } = useWidgetStore(); const { selectedChartId, setSelectedChartId } = useWidgetStore();
const [panelDimensions, setPanelDimensions] = useState<{ const [panelDimensions, setPanelDimensions] = useState<{
[side in Side]?: { width: number; height: number }; [side in Side]?: { width: number; height: number };
@ -87,16 +89,34 @@ export const DraggableWidget = ({
try { try {
const email = localStorage.getItem("email") || ""; const email = localStorage.getItem("email") || "";
const organization = email?.split("@")[1]?.split(".")[0]; const organization = email?.split("@")[1]?.split(".")[0];
const response = await deleteWidgetApi(widget.id, organization); let deleteWidget = {
if (response?.message === "Widget deleted successfully") { zoneId: selectedZone.zoneId,
const updatedWidgets = selectedZone.widgets.filter( widgetID: widget.id,
(w: Widget) => w.id !== widget.id organization: organization
);
setSelectedZone((prevZone: any) => ({
...prevZone,
widgets: updatedWidgets,
}));
} }
if (visualizationSocket) {
visualizationSocket.emit("v2:viz-widget:delete", deleteWidget)
}
const updatedWidgets = selectedZone.widgets.filter(
(w: Widget) => w.id !== widget.id
);
console.log('updatedWidgets: ', updatedWidgets);
setSelectedZone((prevZone: any) => ({
...prevZone,
widgets: updatedWidgets,
}));
setOpenKebabId(null);
// const response = await deleteWidgetApi(widget.id, organization);
// if (response?.message === "Widget deleted successfully") {
// const updatedWidgets = selectedZone.widgets.filter(
// (w: Widget) => w.id !== widget.id
// );
// setSelectedZone((prevZone: any) => ({
// ...prevZone,
// widgets: updatedWidgets,
// }));
// }
} catch (error) { } catch (error) {
} finally { } finally {

View File

@ -35,6 +35,7 @@ export default function Dropped3dWidgets() {
async function get3dWidgetData() { async function get3dWidgetData() {
let result = await get3dWidgetZoneData(selectedZone.zoneId, organization); let result = await get3dWidgetZoneData(selectedZone.zoneId, organization);
console.log('result: ', result);
setWidgets3D(result) setWidgets3D(result)
// Ensure the extracted data has id, type, and position correctly mapped // Ensure the extracted data has id, type, and position correctly mapped
const formattedWidgets = result.map((widget: any) => ({ const formattedWidgets = result.map((widget: any) => ({
@ -51,29 +52,12 @@ export default function Dropped3dWidgets() {
get3dWidgetData(); get3dWidgetData();
}, [selectedZone.zoneId,activeModule]); }, [selectedZone.zoneId, activeModule]);
// useEffect(() => {
// // ✅ Set data only for the selected zone, keeping existing state structure
// setZoneWidgetData((prev) => ({
// ...prev,
// [selectedZone.zoneId]: [
// {
// "id": "1743322674626-50mucpb1c",
// "type": "ui-Widget 1",
// "position": [120.94655021768133, 4.142360029666558, 124.39283546121099]
// },
// {
// "id": "1743322682086-je2h9x33v",
// "type": "ui-Widget 2",
// "position": [131.28751045879255, 0.009999999999970264, 133.92059801984362]
// }
// ]
// }));
// }, [selectedZone.zoneId]); // ✅ Only update when the zone changes
useEffect(() => { useEffect(() => {
if (activeModule !== "visualization") return; if (activeModule !== "visualization") return;
if (widgetSubOption === "Floating") return; if (widgetSubOption === "Floating") return;
if (widgetSubOption === "2D") return;
if (selectedZone.zoneName === "") return if (selectedZone.zoneName === "") return
const canvasElement = gl.domElement; const canvasElement = gl.domElement;
const onDrop = async (event: DragEvent) => { const onDrop = async (event: DragEvent) => {
@ -103,13 +87,15 @@ export default function Dropped3dWidgets() {
let response = await adding3dWidgets(selectedZone.zoneId, organization, newWidget) let response = await adding3dWidgets(selectedZone.zoneId, organization, newWidget)
console.log('response: ', response);
if (response.message === "Widget created successfully") {
// ✅ Store widgets uniquely for each zone // ✅ Store widgets uniquely for each zone
setZoneWidgetData((prev) => ({ setZoneWidgetData((prev) => ({
...prev, ...prev,
[selectedZone.zoneId]: [...(prev[selectedZone.zoneId] || []), newWidget], [selectedZone.zoneId]: [...(prev[selectedZone.zoneId] || []), newWidget],
})); }));
}
} }
}; };
@ -122,6 +108,7 @@ export default function Dropped3dWidgets() {
// Get widgets for the currently active zone // Get widgets for the currently active zone
const activeZoneWidgets = zoneWidgetData[selectedZone.zoneId] || []; const activeZoneWidgets = zoneWidgetData[selectedZone.zoneId] || [];
console.log('activeZoneWidgets: ', activeZoneWidgets);
return ( return (
<> <>

View File

@ -68,6 +68,8 @@ const DroppedObjects: React.FC = () => {
} | null>(null); // State to track the current position during drag } | null>(null); // State to track the current position during drag
const animationRef = useRef<number | null>(null); const animationRef = useRef<number | null>(null);
const { activeModule } = useModuleStore(); const { activeModule } = useModuleStore();
const kebabRef = useRef<HTMLDivElement>(null);
// Clean up animation frame on unmount // Clean up animation frame on unmount
useEffect(() => { useEffect(() => {
@ -77,6 +79,21 @@ const DroppedObjects: React.FC = () => {
} }
}; };
}, []); }, []);
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
if (kebabRef.current && !kebabRef.current.contains(event.target as Node)) {
setOpenKebabId(null);
}
};
// Add event listener when component mounts
document.addEventListener("mousedown", handleClickOutside);
// Clean up event listener when component unmounts
return () => {
document.removeEventListener("mousedown", handleClickOutside);
};
}, []);
const zoneEntries = Object.entries(zones); const zoneEntries = Object.entries(zones);
if (zoneEntries.length === 0) return null; if (zoneEntries.length === 0) return null;
@ -96,7 +113,7 @@ const DroppedObjects: React.FC = () => {
console.log('res: ', res); console.log('res: ', res);
if (res.message === "FloatingWidget deleted successfully") { if (res.message === "FloatingWidget deleted successfully") {
deleteObject(zoneName, index); // Call the deleteObject method from the store deleteObject(zoneName, id, index); // Call the deleteObject method from the store
} }
} catch (error) { } catch (error) {
console.error("Error deleting floating widget:", error); console.error("Error deleting floating widget:", error);
@ -105,7 +122,12 @@ const DroppedObjects: React.FC = () => {
const handlePointerDown = (event: React.PointerEvent, index: number) => { const handlePointerDown = (event: React.PointerEvent, index: number) => {
if ((event.target as HTMLElement).closest(".kebab-options") || (event.target as HTMLElement).closest(".kebab")) {
return; // Prevent dragging when clicking on the kebab menu or its options
}
const obj = zone.objects[index]; const obj = zone.objects[index];
const element = event.currentTarget as HTMLElement;
element.setPointerCapture(event.pointerId);
const container = document.getElementById("real-time-vis-canvas"); const container = document.getElementById("real-time-vis-canvas");
if (!container) return; if (!container) return;
@ -198,13 +220,15 @@ const DroppedObjects: React.FC = () => {
// Update the current position state for DistanceLines // Update the current position state for DistanceLines
setCurrentPosition(newPosition); setCurrentPosition(newPosition);
// Update position immediately without animation frame
updateObjectPosition(zoneName, draggingIndex.index, newPosition);
if (!animationRef.current) { // if (!animationRef.current) {
animationRef.current = requestAnimationFrame(() => { // animationRef.current = requestAnimationFrame(() => {
updateObjectPosition(zoneName, draggingIndex.index, newPosition); // updateObjectPosition(zoneName, draggingIndex.index, newPosition);
animationRef.current = null; // animationRef.current = null;
}); // });
} // }
}; };
const handlePointerUp = async (event: React.PointerEvent<HTMLDivElement>) => { const handlePointerUp = async (event: React.PointerEvent<HTMLDivElement>) => {
@ -246,6 +270,9 @@ const DroppedObjects: React.FC = () => {
...finalPosition, ...finalPosition,
[activeProp1]: finalY, [activeProp1]: finalY,
[activeProp2]: finalX, [activeProp2]: finalX,
// Clear opposite properties
[activeProp1 === "top" ? "bottom" : "top"]: "auto",
[activeProp2 === "left" ? "right" : "left"]: "auto",
}; };
// Save to backend // Save to backend
@ -255,22 +282,35 @@ const DroppedObjects: React.FC = () => {
...zone.objects[draggingIndex.index], ...zone.objects[draggingIndex.index],
position: boundedPosition, position: boundedPosition,
}); });
console.log('response: ', response);
if (response.message === "Widget updated successfully") { if (response.message === "Widget updated successfully") {
updateObjectPosition(zoneName, draggingIndex.index, boundedPosition); updateObjectPosition(zoneName, draggingIndex.index, boundedPosition);
} }
// Clean up // // Clean up
// setDraggingIndex(null);
// setOffset(null);
// setActiveEdges(null); // Clear active edges
// setCurrentPosition(null); // Reset current position
// if (animationRef.current) {
// cancelAnimationFrame(animationRef.current);
// animationRef.current = null;
// }
} catch (error) {
console.error("Error in handlePointerUp:", error);
} finally {
// Clean up regardless of success or failure
setDraggingIndex(null); setDraggingIndex(null);
setOffset(null); setOffset(null);
setActiveEdges(null); // Clear active edges setActiveEdges(null);
setCurrentPosition(null); // Reset current position setCurrentPosition(null);
// Cancel any pending animation frame
if (animationRef.current) { if (animationRef.current) {
cancelAnimationFrame(animationRef.current); cancelAnimationFrame(animationRef.current);
animationRef.current = null; animationRef.current = null;
} }
} catch (error) {
console.error("Error in handlePointerUp:", error);
} }
}; };
@ -279,66 +319,6 @@ const DroppedObjects: React.FC = () => {
setOpenKebabId((prevId) => (prevId === id ? null : id)); setOpenKebabId((prevId) => (prevId === id ? null : id));
}; };
const renderObjectContent = (obj: any) => {
switch (obj.className) {
case "floating total-card":
return (
<>
<div className="header-wrapper">
<div className="header">{obj.header}</div>
<div className="data-values">
<div className="value">{obj.value}</div>
<div className="per">{obj.per}</div>
</div>
</div>
<div className="icon">
<WalletIcon />
</div>
</>
);
case "warehouseThroughput floating":
return (
<>
<div className="header">
<h2>Warehouse Throughput</h2>
<p>
<span>(+5) more</span> in 2025
</p>
</div>
<div className="lineGraph" style={{ height: "100%" }}>
{/* <Line data={lineGraphData} options={lineGraphOptions} /> */}
</div>
</>
);
case "fleetEfficiency floating":
return (
<>
<h2 className="header">Fleet Efficiency</h2>
<div className="progressContainer">
<div className="progress">
<div className="barOverflow">
<div
className="bar"
style={{ transform: `rotate(${obj.value}deg)` }}
></div>
</div>
</div>
</div>
<div className="scaleLabels">
<span>0%</span>
<div className="centerText">
<div className="percentage">{obj.per}%</div>
<div className="status">Optimal</div>
</div>
<span>100%</span>
</div>
</>
);
default:
return null;
}
};
return ( return (
<div <div
onPointerMove={handlePointerMove} onPointerMove={handlePointerMove}
@ -388,12 +368,16 @@ const DroppedObjects: React.FC = () => {
) : null} ) : null}
<div <div
className="icon kebab" className="icon kebab"
onClick={(event) => handleKebabClick(obj.id, event)} ref={kebabRef}
onClick={(event) => {
event.stopPropagation();
handleKebabClick(obj.id, event)
}}
> >
<KebabIcon /> <KebabIcon />
</div> </div>
{openKebabId === obj.id && ( {openKebabId === obj.id && (
<div className="kebab-options"> <div className="kebab-options" ref={kebabRef}>
<div className="dublicate btn" onClick={(event) => { <div className="dublicate btn" onClick={(event) => {
event.stopPropagation(); event.stopPropagation();
handleDuplicate(zoneName, index); // Call the duplicate handler handleDuplicate(zoneName, index); // Call the duplicate handler
@ -414,6 +398,7 @@ const DroppedObjects: React.FC = () => {
</div> </div>
</div> </div>
)} )}
</div> </div>
))} ))}

View File

@ -4,6 +4,7 @@ import { usePlayButtonStore } from "../../../store/usePlayButtonStore";
import { DraggableWidget } from "./DraggableWidget"; import { DraggableWidget } from "./DraggableWidget";
import { arrayMove } from "@dnd-kit/sortable"; import { arrayMove } from "@dnd-kit/sortable";
import { addingWidgets } from "../../../services/realTimeVisulization/zoneData/addWidgets"; import { addingWidgets } from "../../../services/realTimeVisulization/zoneData/addWidgets";
import { useAsset3dWidget, useSocketStore } from "../../../store/store";
type Side = "top" | "bottom" | "left" | "right"; type Side = "top" | "bottom" | "left" | "right";
@ -53,6 +54,7 @@ const Panel: React.FC<PanelProps> = ({
hiddenPanels, hiddenPanels,
setZonesData, setZonesData,
}) => { }) => {
const { widgetSelect, setWidgetSelect } = useAsset3dWidget();
const panelRefs = useRef<{ [side in Side]?: HTMLDivElement }>({}); const panelRefs = useRef<{ [side in Side]?: HTMLDivElement }>({});
const [panelDimensions, setPanelDimensions] = useState<{ const [panelDimensions, setPanelDimensions] = useState<{
[side in Side]?: { width: number; height: number }; [side in Side]?: { width: number; height: number };
@ -60,6 +62,7 @@ const Panel: React.FC<PanelProps> = ({
const [openKebabId, setOpenKebabId] = useState<string | null>(null); const [openKebabId, setOpenKebabId] = useState<string | null>(null);
const { isPlaying } = usePlayButtonStore(); const { isPlaying } = usePlayButtonStore();
const { visualizationSocket } = useSocketStore();
const getPanelStyle = useMemo( const getPanelStyle = useMemo(
() => (side: Side) => { () => (side: Side) => {
@ -100,7 +103,6 @@ const Panel: React.FC<PanelProps> = ({
); );
const handleDrop = (e: React.DragEvent, panel: Side) => { const handleDrop = (e: React.DragEvent, panel: Side) => {
e.preventDefault(); e.preventDefault();
const { draggedAsset } = useWidgetStore.getState(); const { draggedAsset } = useWidgetStore.getState();
if (!draggedAsset) return; if (!draggedAsset) return;
@ -110,7 +112,6 @@ const Panel: React.FC<PanelProps> = ({
const maxCapacity = calculatePanelCapacity(panel); const maxCapacity = calculatePanelCapacity(panel);
if (currentWidgetsCount >= maxCapacity) return; if (currentWidgetsCount >= maxCapacity) return;
addWidgetToPanel(draggedAsset, panel); addWidgetToPanel(draggedAsset, panel);
}; };
@ -147,18 +148,33 @@ const Panel: React.FC<PanelProps> = ({
id: generateUniqueId(), id: generateUniqueId(),
panel, panel,
}; };
try { let addWidget = {
let response = await addingWidgets(selectedZone.zoneId, organization, newWidget); organization: organization,
zoneId: selectedZone.zoneId,
if (response.message === "Widget created successfully") { widget: newWidget
setSelectedZone((prev) => ({
...prev,
widgets: [...prev.widgets, newWidget],
}));
}
} catch (error) {
console.error("Error adding widget:", error);
} }
console.log('newWidget: ', newWidget);
console.log('addWidget: ', addWidget);
if (visualizationSocket) {
visualizationSocket.emit("v2:viz-widget:add", addWidget)
}
setSelectedZone((prev) => ({
...prev,
widgets: [...prev.widgets, newWidget],
}));
// try {
// let response = await addingWidgets(selectedZone.zoneId, organization, newWidget);
// if (response.message === "Widget created successfully") {
// setSelectedZone((prev) => ({
// ...prev,
// widgets: [...prev.widgets, newWidget],
// }));
// }
// } catch (error) {
// console.error("Error adding widget:", error);
// }
}; };

View File

@ -11,6 +11,7 @@ import DroppedObjects from "./DroppedFloatingWidgets";
import { useDroppedObjectsStore } from "../../../store/useDroppedObjectsStore"; import { useDroppedObjectsStore } from "../../../store/useDroppedObjectsStore";
import { import {
useAsset3dWidget, useAsset3dWidget,
useSocketStore,
useWidgetSubOption, useWidgetSubOption,
useZones, useZones,
} from "../../../store/store"; } from "../../../store/store";
@ -18,6 +19,7 @@ import { getZone2dData } from "../../../services/realTimeVisulization/zoneData/g
import { generateUniqueId } from "../../../functions/generateUniqueId"; import { generateUniqueId } from "../../../functions/generateUniqueId";
import { determinePosition } from "./functions/determinePosition"; import { determinePosition } from "./functions/determinePosition";
import { addingFloatingWidgets } from "../../../services/realTimeVisulization/zoneData/addFloatingWidgets"; import { addingFloatingWidgets } from "../../../services/realTimeVisulization/zoneData/addFloatingWidgets";
import SocketRealTimeViz from "../../../modules/visualization/realTimeVizSocket.dev";
type Side = "top" | "bottom" | "left" | "right"; type Side = "top" | "bottom" | "left" | "right";
@ -56,6 +58,7 @@ const RealTimeVisulization: React.FC = () => {
>({}); >({});
const { widgetSelect, setWidgetSelect } = useAsset3dWidget(); const { widgetSelect, setWidgetSelect } = useAsset3dWidget();
const { widgetSubOption, setWidgetSubOption } = useWidgetSubOption(); const { widgetSubOption, setWidgetSubOption } = useWidgetSubOption();
const { visualizationSocket } = useSocketStore();
useEffect(() => { useEffect(() => {
async function GetZoneData() { async function GetZoneData() {
@ -132,6 +135,8 @@ const RealTimeVisulization: React.FC = () => {
const relativeX = event.clientX - canvasRect.left; const relativeX = event.clientX - canvasRect.left;
const relativeY = event.clientY - canvasRect.top; const relativeY = event.clientY - canvasRect.top;
const newPosition = determinePosition(canvasRect, relativeX, relativeY)
console.log('newPosition: ', newPosition);
const newObject = { const newObject = {
...droppedData, ...droppedData,
id: generateUniqueId(), id: generateUniqueId(),
@ -202,6 +207,7 @@ const RealTimeVisulization: React.FC = () => {
<Scene /> <Scene />
</div> </div>
{activeModule === "visualization" && selectedZone.zoneName !== "" && <DroppedObjects />} {activeModule === "visualization" && selectedZone.zoneName !== "" && <DroppedObjects />}
{activeModule === "visualization" && <SocketRealTimeViz />}
{/* <DroppedObjects /> */} {/* <DroppedObjects /> */}
{activeModule === "visualization" && ( {activeModule === "visualization" && (
<> <>

View File

@ -1,69 +1,3 @@
// export function determinePosition(
// canvasRect: DOMRect,
// relativeX: number,
// relativeY: number
// ): {
// top: number | "auto";
// left: number | "auto";
// right: number | "auto";
// bottom: number | "auto";
// } {
// // Calculate the midpoints of the canvas
// const centerX = canvasRect.width / 2;
// const centerY = canvasRect.height / 2;
// // Initialize position with default values
// let position: {
// top: number | "auto";
// left: number | "auto";
// right: number | "auto";
// bottom: number | "auto";
// };
// if (relativeY < centerY) {
// // Top half
// if (relativeX < centerX) {
// // Left side
// position = {
// top: relativeY,
// left: relativeX,
// right: "auto",
// bottom: "auto",
// };
// } else {
// // Right side
// position = {
// top: relativeY,
// right: canvasRect.width - relativeX,
// left: "auto",
// bottom: "auto",
// };
// }
// } else {
// // Bottom half
// if (relativeX < centerX) {
// // Left side
// position = {
// bottom: canvasRect.height - relativeY,
// left: relativeX,
// right: "auto",
// top: "auto",
// };
// } else {
// // Right side
// position = {
// bottom: canvasRect.height - relativeY,
// right: canvasRect.width - relativeX,
// left: "auto",
// top: "auto",
// };
// }
// }
// return position;
// }
export function determinePosition( export function determinePosition(
canvasRect: DOMRect, canvasRect: DOMRect,
@ -89,16 +23,16 @@ export function determinePosition(
if (relativeX < centerX) { if (relativeX < centerX) {
console.log("Top-left"); console.log("Top-left");
position = { position = {
top: relativeY, top: relativeY - 41.5,
left: relativeX, left: relativeX - 125,
right: "auto", right: "auto",
bottom: "auto", bottom: "auto",
}; };
} else { } else {
console.log("Top-right"); console.log("Top-right");
position = { position = {
top: relativeY, top: relativeY - 41.5,
right: canvasRect.width - relativeX, right: canvasRect.width - relativeX - 125,
left: "auto", left: "auto",
bottom: "auto", bottom: "auto",
}; };
@ -107,16 +41,16 @@ export function determinePosition(
if (relativeX < centerX) { if (relativeX < centerX) {
console.log("Bottom-left"); console.log("Bottom-left");
position = { position = {
bottom: canvasRect.height - relativeY, bottom: canvasRect.height - relativeY - 41.5,
left: relativeX, left: relativeX - 125,
right: "auto", right: "auto",
top: "auto", top: "auto",
}; };
} else { } else {
console.log("Bottom-right"); console.log("Bottom-right");
position = { position = {
bottom: canvasRect.height - relativeY, bottom: canvasRect.height - relativeY - 41.5,
right: canvasRect.width - relativeX, right: canvasRect.width - relativeX - 125,
left: "auto", left: "auto",
top: "auto", top: "auto",
}; };

View File

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

View File

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

View File

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

View File

@ -12,18 +12,27 @@ export const useSocketStore = create<any>((set: any, get: any) => ({
} }
const socket = io( const socket = io(
`http://${process.env.REACT_APP_SERVER_SOCKET_API_BASE_URL}/Builder`, `http://${process.env.REACT_APP_SERVER_SOCKET_API_BASE_URL}`,
{ {
reconnection: false, reconnection: false,
auth: { email, organization }, auth: { email, organization },
} }
); );
set({ socket }); const visualizationSocket = io(
`http://${process.env.REACT_APP_SERVER_SOCKET_API_BASE_URL}/Visualization`,
{
reconnection: false,
auth: { email, organization },
}
);
set({ socket, visualizationSocket });
}, },
disconnectSocket: () => { disconnectSocket: () => {
set((state: any) => { set((state: any) => {
state.socket?.disconnect(); state.socket?.disconnect();
state.visualizationSocket?.disconnect();
return { socket: null }; return { socket: null };
}); });
}, },
@ -400,22 +409,3 @@ export const useWidgetSubOption = create<any>((set: any) => ({
widgetSubOption: "2D", widgetSubOption: "2D",
setWidgetSubOption: (x: any) => set({ widgetSubOption: x }), setWidgetSubOption: (x: any) => set({ widgetSubOption: x }),
})); }));
export const useLimitDistance = create<any>((set: any) => ({
limitDistance: true,
setLimitDistance: (x: any) => set({ limitDistance: x }),
}));
export const useTileDistance = create<any>((set: any) => ({
gridValue: { size: 300, divisions: 75 },
planeValue: { height: 300, width: 300 },
setGridValue: (value: any) =>
set((state: any) => ({
gridValue: { ...state.gridValue, ...value },
})),
setPlaneValue: (value: any) =>
set((state: any) => ({
planeValue: { ...state.planeValue, ...value },
})),
}));

View File

@ -36,7 +36,7 @@ type DroppedObjectsState = {
bottom: number | "auto"; bottom: number | "auto";
} }
) => void; ) => void;
deleteObject: (zoneName: string, index: number) => void; // Add this line deleteObject: (zoneName: string, id: string, index: number) => void; // Add this line
duplicateObject: (zoneName: string, index: number) => void; // Add this line duplicateObject: (zoneName: string, index: number) => void; // Add this line
}; };
@ -77,15 +77,16 @@ export const useDroppedObjectsStore = create<DroppedObjectsState>((set) => ({
}; };
}), }),
deleteObject: (zoneName: string, index: number) => deleteObject: (zoneName: string, id: string, index: number) =>
set((state) => { set((state) => {
const zone = state.zones[zoneName]; const zone = state.zones[zoneName];
console.log("zone: ", zone);
if (!zone) return state; if (!zone) return state;
return { return {
zones: { zones: {
[zoneName]: { [zoneName]: {
...zone, ...zone,
objects: zone.objects.filter((_, i) => i !== index), // Remove object at the given index objects: zone.objects.filter((obj) => obj.id !== id), // Remove object at the given index
}, },
}, },
}; };

View File

@ -75,7 +75,7 @@
height: 100vh; // Full viewport height height: 100vh; // Full viewport height
width: 100vw; // Full viewport width width: 100vw; // Full viewport width
overflow: hidden; // Prevent scrollbars overflow: hidden; // Prevent scrollbars
background-color: #232323; background-color: var(--background-color-gray);
} }
// Root overlay styles // Root overlay styles

View File

@ -18,14 +18,4 @@ export function toggleTheme() {
localStorage.setItem('theme', newTheme); localStorage.setItem('theme', newTheme);
} }
// Initialize theme on page load
setTheme(); setTheme();
// Example: Call toggleTheme() when a button is clicked
const toggleSwitch: Element | null = document.querySelector('#theme-switch');
if (toggleSwitch) {
toggleSwitch.addEventListener('click', toggleTheme);
} else {
console.warn("Theme switch button not found!");
}