Merge branch 'main' into simulation-agv
This commit is contained in:
@@ -16,6 +16,7 @@ interface ButtonsProps {
|
||||
zoneName: string;
|
||||
activeSides: Side[];
|
||||
panelOrder: Side[];
|
||||
|
||||
lockedPanels: Side[];
|
||||
zoneId: string;
|
||||
zoneViewPortTarget: number[];
|
||||
@@ -33,6 +34,7 @@ interface ButtonsProps {
|
||||
zoneName: string;
|
||||
activeSides: Side[];
|
||||
panelOrder: Side[];
|
||||
|
||||
lockedPanels: Side[];
|
||||
zoneId: string;
|
||||
zoneViewPortTarget: number[];
|
||||
@@ -120,23 +122,39 @@ const AddButtons: React.FC<ButtonsProps> = ({
|
||||
console.log("updatedZone: ", updatedZone);
|
||||
setSelectedZone(updatedZone);
|
||||
} else {
|
||||
// If the panel is not active, activate it
|
||||
const newActiveSides = [...selectedZone.activeSides, side];
|
||||
const updatePanelData = async () => {
|
||||
try {
|
||||
// Get email and organization safely
|
||||
const email = localStorage.getItem("email") || "";
|
||||
const organization = email?.split("@")[1]?.split(".")[0] || "defaultOrg"; // Fallback value
|
||||
|
||||
const updatedZone = {
|
||||
...selectedZone,
|
||||
activeSides: newActiveSides,
|
||||
panelOrder: newActiveSides,
|
||||
// Prevent duplicate side entries
|
||||
const newActiveSides = selectedZone.activeSides.includes(side)
|
||||
? [...selectedZone.activeSides]
|
||||
: [...selectedZone.activeSides, side];
|
||||
|
||||
const updatedZone = {
|
||||
...selectedZone,
|
||||
activeSides: newActiveSides,
|
||||
panelOrder: newActiveSides,
|
||||
};
|
||||
|
||||
// API call
|
||||
const response = await panelData(organization, selectedZone.zoneId, newActiveSides);
|
||||
console.log("response: ", response);
|
||||
|
||||
// Update state
|
||||
console.log("updatedZone: ", updatedZone);
|
||||
setSelectedZone(updatedZone);
|
||||
} catch (error) {
|
||||
console.error("Error updating panel data:", error);
|
||||
}
|
||||
};
|
||||
const email = localStorage.getItem("email");
|
||||
const organization = email!.split("@")[1].split(".")[0];
|
||||
// let response = panelData(organization, selectedZone.zoneId, newActiveSides)
|
||||
// console.log('response: ', response);
|
||||
|
||||
// Update the selectedZone state
|
||||
console.log("updatedZone: ", updatedZone);
|
||||
setSelectedZone(updatedZone);
|
||||
updatePanelData(); // Call the async function
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -146,9 +164,8 @@ const AddButtons: React.FC<ButtonsProps> = ({
|
||||
<div key={side} className={`side-button-container ${side}`}>
|
||||
{/* "+" Button */}
|
||||
<button
|
||||
className={`side-button ${side}${
|
||||
selectedZone.activeSides.includes(side) ? " active" : ""
|
||||
}`}
|
||||
className={`side-button ${side}${selectedZone.activeSides.includes(side) ? " active" : ""
|
||||
}`}
|
||||
onClick={() => handlePlusButtonClick(side)}
|
||||
title={
|
||||
selectedZone.activeSides.includes(side)
|
||||
@@ -166,9 +183,8 @@ const AddButtons: React.FC<ButtonsProps> = ({
|
||||
<div className="extra-Bs">
|
||||
{/* Hide Panel */}
|
||||
<div
|
||||
className={`icon ${
|
||||
hiddenPanels.includes(side) ? "active" : ""
|
||||
}`}
|
||||
className={`icon ${hiddenPanels.includes(side) ? "active" : ""
|
||||
}`}
|
||||
title={
|
||||
hiddenPanels.includes(side) ? "Show Panel" : "Hide Panel"
|
||||
}
|
||||
@@ -190,9 +206,8 @@ const AddButtons: React.FC<ButtonsProps> = ({
|
||||
|
||||
{/* Lock/Unlock Panel */}
|
||||
<div
|
||||
className={`icon ${
|
||||
selectedZone.lockedPanels.includes(side) ? "active" : ""
|
||||
}`}
|
||||
className={`icon ${selectedZone.lockedPanels.includes(side) ? "active" : ""
|
||||
}`}
|
||||
title={
|
||||
selectedZone.lockedPanels.includes(side)
|
||||
? "Unlock Panel"
|
||||
|
||||
@@ -3,6 +3,7 @@ import { Widget } from "../../../store/useWidgetStore";
|
||||
import { MoveArrowLeft, MoveArrowRight } from "../../icons/SimulationIcons";
|
||||
import { InfoIcon } from "../../icons/ExportCommonIcons";
|
||||
import { useDroppedObjectsStore } from "../../../store/useDroppedObjectsStore";
|
||||
import { getSelect2dZoneData } from "../../../services/realTimeVisulization/zoneData/getSelect2dZoneData";
|
||||
|
||||
// Define the type for `Side`
|
||||
type Side = "top" | "bottom" | "left" | "right";
|
||||
@@ -12,6 +13,7 @@ interface DisplayZoneProps {
|
||||
[key: string]: {
|
||||
activeSides: Side[];
|
||||
panelOrder: Side[];
|
||||
|
||||
lockedPanels: Side[];
|
||||
widgets: Widget[];
|
||||
zoneId: string;
|
||||
@@ -23,6 +25,7 @@ interface DisplayZoneProps {
|
||||
zoneName: string;
|
||||
activeSides: Side[];
|
||||
panelOrder: Side[];
|
||||
|
||||
lockedPanels: Side[];
|
||||
zoneId: string;
|
||||
zoneViewPortTarget: number[];
|
||||
@@ -40,6 +43,7 @@ interface DisplayZoneProps {
|
||||
zoneName: string;
|
||||
activeSides: Side[];
|
||||
panelOrder: Side[];
|
||||
|
||||
lockedPanels: Side[];
|
||||
zoneId: string;
|
||||
zoneViewPortTarget: number[];
|
||||
@@ -60,6 +64,7 @@ const DisplayZone: React.FC<DisplayZoneProps> = ({
|
||||
selectedZone,
|
||||
setSelectedZone,
|
||||
}) => {
|
||||
|
||||
// Ref for the container element
|
||||
const containerRef = useRef<HTMLDivElement | null>(null);
|
||||
|
||||
@@ -141,8 +146,27 @@ const DisplayZone: React.FC<DisplayZoneProps> = ({
|
||||
}
|
||||
};
|
||||
|
||||
async function handleSelect2dZoneData(zoneId: string, zoneName: string) {
|
||||
const email = localStorage.getItem("email") || "";
|
||||
const organization = email?.split("@")[1]?.split(".")[0]
|
||||
let response = await getSelect2dZoneData(zoneId, organization)
|
||||
setSelectedZone({
|
||||
zoneName,
|
||||
activeSides: response.activeSides,
|
||||
panelOrder: response.panelOrder,
|
||||
lockedPanels: response.lockedPanels,
|
||||
widgets: response.widgets,
|
||||
zoneId: zoneId,
|
||||
zoneViewPortTarget:
|
||||
response.viewPortCenter,
|
||||
zoneViewPortPosition:
|
||||
response.viewPortposition,
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={containerRef}
|
||||
className={`zone-wrapper ${
|
||||
selectedZone?.activeSides?.includes("bottom") && "bottom"
|
||||
}`}
|
||||
@@ -160,23 +184,11 @@ const DisplayZone: React.FC<DisplayZoneProps> = ({
|
||||
{Object.keys(zonesData).map((zoneName, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className={`zone ${
|
||||
selectedZone.zoneName === zoneName ? "active" : ""
|
||||
}`}
|
||||
className={`zone ${selectedZone.zoneName === zoneName ? "active" : ""
|
||||
}`}
|
||||
onClick={() => {
|
||||
useDroppedObjectsStore.getState().setZone(zoneName, zonesData[zoneName]?.zoneId);
|
||||
setSelectedZone({
|
||||
zoneName,
|
||||
activeSides: zonesData[zoneName].activeSides || [],
|
||||
panelOrder: zonesData[zoneName].panelOrder || [],
|
||||
lockedPanels: zonesData[zoneName].lockedPanels || [],
|
||||
widgets: zonesData[zoneName].widgets || [],
|
||||
zoneId: zonesData[zoneName]?.zoneId || "",
|
||||
zoneViewPortTarget:
|
||||
zonesData[zoneName].zoneViewPortTarget || [],
|
||||
zoneViewPortPosition:
|
||||
zonesData[zoneName].zoneViewPortPosition || [],
|
||||
});
|
||||
handleSelect2dZoneData(zonesData[zoneName]?.zoneId, zoneName)
|
||||
}}
|
||||
>
|
||||
{zoneName}
|
||||
|
||||
@@ -36,6 +36,7 @@ export const DraggableWidget = ({
|
||||
zoneName: string;
|
||||
activeSides: Side[];
|
||||
panelOrder: Side[];
|
||||
|
||||
lockedPanels: Side[];
|
||||
widgets: Widget[];
|
||||
};
|
||||
@@ -44,6 +45,7 @@ export const DraggableWidget = ({
|
||||
zoneName: string;
|
||||
activeSides: Side[];
|
||||
panelOrder: Side[];
|
||||
|
||||
lockedPanels: Side[];
|
||||
zoneId: string;
|
||||
zoneViewPortTarget: number[];
|
||||
@@ -78,9 +80,11 @@ export const DraggableWidget = ({
|
||||
const isPanelHidden = hiddenPanels.includes(widget.panel);
|
||||
|
||||
const deleteSelectedChart = () => {
|
||||
console.log('widget.id: ', widget.id);
|
||||
const updatedWidgets = selectedZone.widgets.filter(
|
||||
(w: Widget) => w.id !== widget.id
|
||||
);
|
||||
console.log('updatedWidgets: ', updatedWidgets);
|
||||
|
||||
setSelectedZone((prevZone: any) => ({
|
||||
...prevZone,
|
||||
@@ -122,7 +126,6 @@ export const DraggableWidget = ({
|
||||
...widget,
|
||||
id: `${widget.id}-copy-${Date.now()}`,
|
||||
};
|
||||
|
||||
setSelectedZone((prevZone: any) => ({
|
||||
...prevZone,
|
||||
widgets: [...prevZone.widgets, duplicatedWidget],
|
||||
@@ -130,7 +133,7 @@ export const DraggableWidget = ({
|
||||
|
||||
setOpenKebabId(null);
|
||||
|
||||
console.log("Duplicated widget with ID:", duplicatedWidget.id);
|
||||
|
||||
};
|
||||
|
||||
const handleKebabClick = (event: React.MouseEvent<HTMLDivElement>) => {
|
||||
@@ -173,6 +176,7 @@ export const DraggableWidget = ({
|
||||
};
|
||||
|
||||
const handleDrop = (event: React.DragEvent<HTMLDivElement>) => {
|
||||
|
||||
event.preventDefault();
|
||||
const fromIndex = parseInt(event.dataTransfer.getData("text/plain"), 10); // Get the dragged widget's index
|
||||
const toIndex = index; // The index of the widget where the drop occurred
|
||||
@@ -186,9 +190,8 @@ export const DraggableWidget = ({
|
||||
<div
|
||||
draggable
|
||||
key={widget.id}
|
||||
className={`chart-container ${
|
||||
selectedChartId?.id === widget.id && "activeChart"
|
||||
}`}
|
||||
className={`chart-container ${selectedChartId?.id === widget.id && "activeChart"
|
||||
}`}
|
||||
onPointerDown={handlePointerDown}
|
||||
onDragStart={handleDragStart}
|
||||
onDragEnter={handleDragEnter}
|
||||
@@ -208,9 +211,8 @@ export const DraggableWidget = ({
|
||||
{openKebabId === widget.id && (
|
||||
<div className="kebab-options" ref={widgetRef}>
|
||||
<div
|
||||
className={`edit btn ${
|
||||
isPanelFull(widget.panel) ? "btn-blur" : ""
|
||||
}`}
|
||||
className={`edit btn ${isPanelFull(widget.panel) ? "btn-blur" : ""
|
||||
}`}
|
||||
onClick={isPanelFull(widget.panel) ? undefined : duplicateWidget}
|
||||
>
|
||||
<div className="icon">
|
||||
@@ -237,14 +239,14 @@ export const DraggableWidget = ({
|
||||
title={widget.title}
|
||||
fontSize={widget.fontSize}
|
||||
fontWeight={widget.fontWeight}
|
||||
data={{
|
||||
measurements: [
|
||||
{ name: "testDevice", fields: "powerConsumption" },
|
||||
{ name: "furnace", fields: "powerConsumption" },
|
||||
],
|
||||
interval: 1000,
|
||||
duration: "1h",
|
||||
}}
|
||||
// data={{
|
||||
// measurements: [
|
||||
// { name: "testDevice", fields: "powerConsumption" },
|
||||
// { name: "furnace", fields: "powerConsumption" },
|
||||
// ],
|
||||
// interval: 1000,
|
||||
// duration: "1h",
|
||||
// }}
|
||||
/>
|
||||
)}
|
||||
{widget.type === "bar" && (
|
||||
@@ -253,14 +255,14 @@ export const DraggableWidget = ({
|
||||
title={widget.title}
|
||||
fontSize={widget.fontSize}
|
||||
fontWeight={widget.fontWeight}
|
||||
data={{
|
||||
measurements: [
|
||||
{ name: "testDevice", fields: "powerConsumption" },
|
||||
{ name: "furnace", fields: "powerConsumption" },
|
||||
],
|
||||
interval: 1000,
|
||||
duration: "1h",
|
||||
}}
|
||||
// data={{
|
||||
// measurements: [
|
||||
// { name: "testDevice", fields: "powerConsumption" },
|
||||
// { name: "furnace", fields: "powerConsumption" },
|
||||
// ],
|
||||
// interval: 1000,
|
||||
// duration: "1h",
|
||||
// }}
|
||||
/>
|
||||
)}
|
||||
{widget.type === "pie" && (
|
||||
@@ -269,14 +271,14 @@ export const DraggableWidget = ({
|
||||
title={widget.title}
|
||||
fontSize={widget.fontSize}
|
||||
fontWeight={widget.fontWeight}
|
||||
data={{
|
||||
measurements: [
|
||||
{ name: "testDevice", fields: "powerConsumption" },
|
||||
{ name: "furnace", fields: "powerConsumption" },
|
||||
],
|
||||
interval: 1000,
|
||||
duration: "1h",
|
||||
}}
|
||||
// data={{
|
||||
// measurements: [
|
||||
// { name: "testDevice", fields: "powerConsumption" },
|
||||
// { name: "furnace", fields: "powerConsumption" },
|
||||
// ],
|
||||
// interval: 1000,
|
||||
// duration: "1h",
|
||||
// }}
|
||||
/>
|
||||
)}
|
||||
{widget.type === "doughnut" && (
|
||||
|
||||
79
app/src/components/ui/componets/Dropped3dWidget.tsx
Normal file
79
app/src/components/ui/componets/Dropped3dWidget.tsx
Normal file
@@ -0,0 +1,79 @@
|
||||
|
||||
import { useThree } from "@react-three/fiber";
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { useAsset3dWidget } from "../../../store/store";
|
||||
import useModuleStore from "../../../store/useModuleStore";
|
||||
import { ThreeState } from "../../../types/world/worldTypes";
|
||||
import * as THREE from "three";
|
||||
import Throughput from "../../layout/3D-cards/cards/Throughput";
|
||||
import ProductionCapacity from "../../layout/3D-cards/cards/ProductionCapacity";
|
||||
import ReturnOfInvestment from "../../layout/3D-cards/cards/ReturnOfInvestment";
|
||||
import StateWorking from "../../layout/3D-cards/cards/StateWorking";
|
||||
|
||||
export default function Dropped3dWidgets() {
|
||||
const { widgetSelect } = useAsset3dWidget();
|
||||
const { activeModule } = useModuleStore();
|
||||
const { raycaster, gl, scene }: ThreeState = useThree();
|
||||
|
||||
// 🔥 Store multiple instances per widget type
|
||||
const [widgetPositions, setWidgetPositions] = useState<Record<string, [number, number, number][]>>({});
|
||||
|
||||
useEffect(() => {
|
||||
if (activeModule !== "visualization") return;
|
||||
const canvasElement = gl.domElement;
|
||||
|
||||
const onDrop = (event: DragEvent) => {
|
||||
event.preventDefault(); // Prevent default browser behavior
|
||||
|
||||
if (!widgetSelect.startsWith("ui")) return;
|
||||
|
||||
|
||||
const group1 = scene.getObjectByName("itemsGroup");
|
||||
if (!group1) return;
|
||||
|
||||
|
||||
|
||||
const Assets = group1.children
|
||||
.map((val) => scene.getObjectByProperty("uuid", val.uuid))
|
||||
.filter(Boolean) as THREE.Object3D[];
|
||||
|
||||
|
||||
const intersects = raycaster.intersectObjects(Assets);
|
||||
|
||||
if (intersects.length > 0) {
|
||||
const { x, y, z } = intersects[0].point;
|
||||
|
||||
|
||||
// ✅ Allow multiple instances by storing positions in an array
|
||||
setWidgetPositions((prev) => ({
|
||||
...prev,
|
||||
[widgetSelect]: [...(prev[widgetSelect] || []), [x, y, z]],
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
canvasElement.addEventListener("drop", onDrop);
|
||||
|
||||
return () => {
|
||||
canvasElement.removeEventListener("drop", onDrop);
|
||||
};
|
||||
}, [widgetSelect, activeModule]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{widgetPositions["ui-Widget 1"]?.map((pos, index) => (
|
||||
<ProductionCapacity key={`Widget1-${index}`} position={pos} />
|
||||
))}
|
||||
{widgetPositions["ui-Widget 2"]?.map((pos, index) => (
|
||||
<ReturnOfInvestment key={`Widget2-${index}`} position={pos} />
|
||||
))}
|
||||
{widgetPositions["ui-Widget 3"]?.map((pos, index) => (
|
||||
<StateWorking key={`Widget3-${index}`} position={pos} />
|
||||
))}
|
||||
{widgetPositions["ui-Widget 4"]?.map((pos, index) => (
|
||||
<Throughput key={`Widget4-${index}`} position={pos} />
|
||||
))}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ import { useDroppedObjectsStore, Zones } from "../../../store/useDroppedObjectsS
|
||||
|
||||
const DroppedObjects: React.FC = () => {
|
||||
const zones = useDroppedObjectsStore((state) => state.zones);
|
||||
|
||||
const updateObjectPosition = useDroppedObjectsStore((state) => state.updateObjectPosition);
|
||||
const [draggingIndex, setDraggingIndex] = useState<{ zone: string; index: number } | null>(null);
|
||||
const [offset, setOffset] = useState<[number, number] | null>(null);
|
||||
|
||||
@@ -3,6 +3,7 @@ import { useWidgetStore } from "../../../store/useWidgetStore";
|
||||
import { usePlayButtonStore } from "../../../store/usePlayButtonStore";
|
||||
import { DraggableWidget } from "./DraggableWidget";
|
||||
import { arrayMove } from "@dnd-kit/sortable";
|
||||
import { addingWidgets } from "../../../services/realTimeVisulization/zoneData/addWidgets";
|
||||
|
||||
type Side = "top" | "bottom" | "left" | "right";
|
||||
|
||||
@@ -19,6 +20,7 @@ interface PanelProps {
|
||||
zoneName: string;
|
||||
activeSides: Side[];
|
||||
panelOrder: Side[];
|
||||
|
||||
lockedPanels: Side[];
|
||||
zoneId: string;
|
||||
zoneViewPortTarget: number[];
|
||||
@@ -30,6 +32,7 @@ interface PanelProps {
|
||||
zoneName: string;
|
||||
activeSides: Side[];
|
||||
panelOrder: Side[];
|
||||
|
||||
lockedPanels: Side[];
|
||||
zoneId: string;
|
||||
zoneViewPortTarget: number[];
|
||||
@@ -72,9 +75,8 @@ const Panel: React.FC<PanelProps> = ({
|
||||
case "top":
|
||||
case "bottom":
|
||||
return {
|
||||
width: `calc(100% - ${
|
||||
(leftActive ? panelSize : 0) + (rightActive ? panelSize : 0)
|
||||
}px)`,
|
||||
width: `calc(100% - ${(leftActive ? panelSize : 0) + (rightActive ? panelSize : 0)
|
||||
}px)`,
|
||||
height: `${panelSize - 2}px`,
|
||||
left: leftActive ? `${panelSize}px` : "0",
|
||||
right: rightActive ? `${panelSize}px` : "0",
|
||||
@@ -84,9 +86,8 @@ const Panel: React.FC<PanelProps> = ({
|
||||
case "right":
|
||||
return {
|
||||
width: `${panelSize - 2}px`,
|
||||
height: `calc(100% - ${
|
||||
(topActive ? panelSize : 0) + (bottomActive ? panelSize : 0)
|
||||
}px)`,
|
||||
height: `calc(100% - ${(topActive ? panelSize : 0) + (bottomActive ? panelSize : 0)
|
||||
}px)`,
|
||||
top: topActive ? `${panelSize}px` : "0",
|
||||
bottom: bottomActive ? `${panelSize}px` : "0",
|
||||
[side]: "0",
|
||||
@@ -99,6 +100,7 @@ const Panel: React.FC<PanelProps> = ({
|
||||
);
|
||||
|
||||
const handleDrop = (e: React.DragEvent, panel: Side) => {
|
||||
|
||||
e.preventDefault();
|
||||
const { draggedAsset } = useWidgetStore.getState();
|
||||
if (!draggedAsset) return;
|
||||
@@ -109,8 +111,6 @@ const Panel: React.FC<PanelProps> = ({
|
||||
|
||||
if (currentWidgetsCount >= maxCapacity) return;
|
||||
|
||||
console.log("draggedAsset: ", draggedAsset);
|
||||
console.log("panel: ", panel);
|
||||
addWidgetToPanel(draggedAsset, panel);
|
||||
};
|
||||
|
||||
@@ -139,17 +139,27 @@ const Panel: React.FC<PanelProps> = ({
|
||||
};
|
||||
|
||||
// while dublicate check this and add
|
||||
const addWidgetToPanel = (asset: any, panel: Side) => {
|
||||
const addWidgetToPanel = async (asset: any, panel: Side) => {
|
||||
const email = localStorage.getItem("email") || "";
|
||||
const organization = email?.split("@")[1]?.split(".")[0]
|
||||
const newWidget = {
|
||||
...asset,
|
||||
id: generateUniqueId(),
|
||||
panel,
|
||||
};
|
||||
try {
|
||||
let response = await addingWidgets(selectedZone.zoneId, organization, newWidget);
|
||||
console.log("response: ", response);
|
||||
if (response.message === "Widget created successfully") {
|
||||
setSelectedZone((prev) => ({
|
||||
...prev,
|
||||
widgets: [...prev.widgets, newWidget],
|
||||
}));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error adding widget:", error);
|
||||
}
|
||||
|
||||
setSelectedZone((prev) => ({
|
||||
...prev,
|
||||
widgets: [...prev.widgets, newWidget],
|
||||
}));
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
@@ -180,7 +190,7 @@ const Panel: React.FC<PanelProps> = ({
|
||||
|
||||
const handleReorder = (fromIndex: number, toIndex: number, panel: Side) => {
|
||||
if (!selectedZone) return; // Ensure selectedZone is not null
|
||||
console.log("selectedZone: ", selectedZone);
|
||||
|
||||
|
||||
setSelectedZone((prev) => {
|
||||
if (!prev) return prev; // Ensure prev is not null
|
||||
|
||||
@@ -9,7 +9,9 @@ import useModuleStore from "../../../store/useModuleStore";
|
||||
|
||||
import DroppedObjects from "./DroppedFloatingWidgets";
|
||||
import { useDroppedObjectsStore } from "../../../store/useDroppedObjectsStore";
|
||||
import { useZones } from "../../../store/store";
|
||||
import { useAsset3dWidget, useZones } from "../../../store/store";
|
||||
import { getZoneData } from "../../../services/realTimeVisulization/zoneData/getZones";
|
||||
import { getZone2dData } from "../../../services/realTimeVisulization/zoneData/getZoneData";
|
||||
|
||||
|
||||
type Side = "top" | "bottom" | "left" | "right";
|
||||
@@ -19,6 +21,7 @@ type FormattedZoneData = Record<
|
||||
{
|
||||
activeSides: Side[];
|
||||
panelOrder: Side[];
|
||||
|
||||
lockedPanels: Side[];
|
||||
zoneId: string;
|
||||
zoneViewPortTarget: number[];
|
||||
@@ -43,25 +46,38 @@ const RealTimeVisulization: React.FC = () => {
|
||||
const [zonesData, setZonesData] = useState<FormattedZoneData>({});
|
||||
const { selectedZone, setSelectedZone } = useSelectedZoneStore();
|
||||
const { zones } = useZones()
|
||||
|
||||
const [floatingWidgets, setFloatingWidgets] = useState<Record<string, { zoneName: string; zoneId: string; objects: any[] }>>({});
|
||||
const { widgetSelect, setWidgetSelect } = useAsset3dWidget();
|
||||
useEffect(() => {
|
||||
const data = Array.isArray(zones) ? zones : [];
|
||||
async function GetZoneData() {
|
||||
const email = localStorage.getItem("email") || "";
|
||||
const organization = email?.split("@")[1]?.split(".")[0];
|
||||
try {
|
||||
const response = await getZone2dData(organization);
|
||||
if (!Array.isArray(response)) {
|
||||
return;
|
||||
}
|
||||
const formattedData = response.reduce<FormattedZoneData>((acc, zone) => {
|
||||
acc[zone.zoneName] = {
|
||||
activeSides: [],
|
||||
panelOrder: [],
|
||||
lockedPanels: [],
|
||||
zoneId: zone.zoneId,
|
||||
zoneViewPortTarget: zone.viewPortCenter,
|
||||
zoneViewPortPosition: zone.viewPortposition,
|
||||
widgets: [],
|
||||
};
|
||||
return acc;
|
||||
}, {});
|
||||
setZonesData(formattedData);
|
||||
} catch (error) {
|
||||
console.log('error: ', error);
|
||||
}
|
||||
}
|
||||
|
||||
const formattedData = data.reduce<FormattedZoneData>((acc, zone) => {
|
||||
acc[zone.zoneName] = {
|
||||
activeSides: [],
|
||||
panelOrder: [],
|
||||
lockedPanels: [],
|
||||
zoneId: zone.zoneId,
|
||||
zoneViewPortTarget: zone.viewPortCenter,
|
||||
zoneViewPortPosition: zone.viewPortposition,
|
||||
widgets: [],
|
||||
};
|
||||
return acc;
|
||||
}, {});
|
||||
GetZoneData();
|
||||
}, []); // Removed `zones` from dependencies
|
||||
|
||||
setZonesData(formattedData);
|
||||
}, [zones]);
|
||||
|
||||
useEffect(() => {
|
||||
setZonesData((prev) => {
|
||||
@@ -81,37 +97,47 @@ const RealTimeVisulization: React.FC = () => {
|
||||
};
|
||||
});
|
||||
}, [selectedZone]);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
}, [floatingWidgets])
|
||||
|
||||
const handleDrop = (event: React.DragEvent<HTMLDivElement>) => {
|
||||
event.preventDefault();
|
||||
const data = event.dataTransfer.getData("text/plain");
|
||||
if (!data || !selectedZone.zoneName) return;
|
||||
|
||||
if (widgetSelect !== "") return;
|
||||
if (!data || selectedZone.zoneName === "") return;
|
||||
|
||||
const droppedData = JSON.parse(data);
|
||||
const canvasElement = document.getElementById("real-time-vis-canvas");
|
||||
if (!canvasElement) return;
|
||||
|
||||
|
||||
const canvasRect = canvasElement.getBoundingClientRect();
|
||||
const relativeX = event.clientX - canvasRect.left;
|
||||
const relativeY = event.clientY - canvasRect.top;
|
||||
|
||||
|
||||
const newObject = {
|
||||
...droppedData,
|
||||
position: [relativeY, relativeX], // Y first because of top/left style
|
||||
};
|
||||
|
||||
console.log("newObject: ", newObject);
|
||||
|
||||
// Only set zone if it’s not already in the store (prevents overwriting objects)
|
||||
const existingZone = useDroppedObjectsStore.getState().zones[selectedZone.zoneName];
|
||||
if (!existingZone) {
|
||||
useDroppedObjectsStore.getState().setZone(selectedZone.zoneName, selectedZone.zoneId);
|
||||
}
|
||||
|
||||
// Add the dropped object to the zone
|
||||
useDroppedObjectsStore.getState().addObject(selectedZone.zoneName, newObject);
|
||||
setFloatingWidgets((prevWidgets) => ({
|
||||
...prevWidgets,
|
||||
[selectedZone.zoneName]: {
|
||||
...prevWidgets[selectedZone.zoneName],
|
||||
zoneName: selectedZone.zoneName,
|
||||
zoneId: selectedZone.zoneId,
|
||||
objects: [...(prevWidgets[selectedZone.zoneName]?.objects || []), newObject],
|
||||
},
|
||||
}));
|
||||
};
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -123,6 +149,7 @@ const RealTimeVisulization: React.FC = () => {
|
||||
width: isPlaying || activeModule !== "visualization" ? "100vw" : "",
|
||||
left: isPlaying || activeModule !== "visualization" ? "0%" : "",
|
||||
}}
|
||||
|
||||
>
|
||||
<div
|
||||
className="scene-container"
|
||||
|
||||
@@ -1,6 +1,195 @@
|
||||
import { useMemo } from "react";
|
||||
// import React, { useEffect, useRef, useMemo, useState } from "react";
|
||||
// import { Chart } from "chart.js/auto";
|
||||
// import { useThemeStore } from "../../../../store/useThemeStore";
|
||||
// import io from "socket.io-client";
|
||||
// import { Bar } from 'react-chartjs-2';
|
||||
// import useChartStore from "../../../../store/useChartStore";
|
||||
|
||||
// // WebSocket Connection
|
||||
// // const socket = io("http://localhost:5000"); // Adjust to your backend URL
|
||||
|
||||
// interface ChartComponentProps {
|
||||
// type: any;
|
||||
// title: string;
|
||||
// fontFamily?: string;
|
||||
// fontSize?: string;
|
||||
// fontWeight?: "Light" | "Regular" | "Bold";
|
||||
// data: any;
|
||||
// }
|
||||
|
||||
// const LineGraphComponent = ({
|
||||
// type,
|
||||
// title,
|
||||
// fontFamily,
|
||||
// fontSize,
|
||||
// fontWeight = "Regular",
|
||||
// data,
|
||||
// }: ChartComponentProps) => {
|
||||
// const canvasRef = useRef<HTMLCanvasElement>(null);
|
||||
// const { themeColor } = useThemeStore();
|
||||
// const [chartData, setChartData] = useState<{ labels: string[]; datasets: any[] }>({
|
||||
// labels: [],
|
||||
// datasets: [],
|
||||
// });
|
||||
|
||||
// const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL;
|
||||
|
||||
// const defaultData = {
|
||||
// labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
|
||||
// datasets: [
|
||||
// {
|
||||
// label: "Dataset",
|
||||
// data: [12, 19, 3, 5, 2, 3],
|
||||
// backgroundColor: ["#6f42c1"],
|
||||
// borderColor: "#ffffff",
|
||||
// borderWidth: 2,
|
||||
// },
|
||||
// ],
|
||||
// };
|
||||
|
||||
// // Memoize Theme Colors to Prevent Unnecessary Recalculations
|
||||
// const buttonActionColor = useMemo(
|
||||
// () => themeColor[0] || "#5c87df",
|
||||
// [themeColor]
|
||||
// );
|
||||
// const buttonAbortColor = useMemo(
|
||||
// () => themeColor[1] || "#ffffff",
|
||||
// [themeColor]
|
||||
// );
|
||||
|
||||
// // Memoize Font Weight Mapping
|
||||
// const chartFontWeightMap = useMemo(
|
||||
// () => ({
|
||||
// Light: "lighter" as const,
|
||||
// Regular: "normal" as const,
|
||||
// Bold: "bold" as const,
|
||||
// }),
|
||||
// []
|
||||
// );
|
||||
|
||||
// // Parse and Memoize Font Size
|
||||
// const fontSizeValue = useMemo(
|
||||
// () => (fontSize ? parseInt(fontSize) : 12),
|
||||
// [fontSize]
|
||||
// );
|
||||
|
||||
// // Determine and Memoize Font Weight
|
||||
// const fontWeightValue = useMemo(
|
||||
// () => chartFontWeightMap[fontWeight],
|
||||
// [fontWeight, chartFontWeightMap]
|
||||
// );
|
||||
|
||||
// // Memoize Chart Font Style
|
||||
// const chartFontStyle = useMemo(
|
||||
// () => ({
|
||||
// family: fontFamily || "Arial",
|
||||
// size: fontSizeValue,
|
||||
// weight: fontWeightValue,
|
||||
// }),
|
||||
// [fontFamily, fontSizeValue, fontWeightValue]
|
||||
// );
|
||||
|
||||
// // Memoize Chart Data
|
||||
// // const data = useMemo(() => propsData, [propsData]);
|
||||
|
||||
// // Memoize Chart Options
|
||||
// const options = useMemo(
|
||||
// () => ({
|
||||
// responsive: true,
|
||||
// maintainAspectRatio: false,
|
||||
// plugins: {
|
||||
// title: {
|
||||
// display: true,
|
||||
// text: title,
|
||||
// font: chartFontStyle,
|
||||
// },
|
||||
// legend: {
|
||||
// display: false,
|
||||
// },
|
||||
// },
|
||||
// scales: {
|
||||
// x: {
|
||||
// ticks: {
|
||||
// display: true, // This hides the x-axis labels
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// }),
|
||||
// [title, chartFontStyle]
|
||||
// );
|
||||
|
||||
// const { measurements, setMeasurements, updateDuration, duration } = useChartStore();
|
||||
|
||||
// useEffect(() => {
|
||||
|
||||
// const socket = io(`http://${iotApiUrl}`);
|
||||
|
||||
// if ( measurements.length > 0 ) {
|
||||
// var inputes = {
|
||||
// measurements: measurements,
|
||||
// duration: duration,
|
||||
// interval: 1000,
|
||||
// }
|
||||
|
||||
// // Start stream
|
||||
// const startStream = () => {
|
||||
// socket.emit("lineInput", inputes);
|
||||
// }
|
||||
|
||||
// socket.on('connect', startStream);
|
||||
|
||||
// socket.on("lineOutput", (response) => {
|
||||
// const responceData = response.data;
|
||||
// console.log("Received data:", responceData);
|
||||
|
||||
// // Extract timestamps and values
|
||||
// const labels = responceData.time;
|
||||
// const datasets = measurements.map((measurement: any) => {
|
||||
// const key = `${measurement.name}.${measurement.fields}`;
|
||||
// return {
|
||||
// label: key,
|
||||
// data: responceData[key]?.values ?? [], // Ensure it exists
|
||||
// backgroundColor: "#6f42c1",
|
||||
// borderColor: "#ffffff",
|
||||
// };
|
||||
// });
|
||||
|
||||
// setChartData({ labels, datasets });
|
||||
// });
|
||||
// }
|
||||
|
||||
// return () => {
|
||||
// socket.off("lineOutput");
|
||||
// socket.emit("stop_stream"); // Stop streaming when component unmounts
|
||||
// };
|
||||
// }, [measurements, duration]);
|
||||
|
||||
// // useEffect(() => {
|
||||
// // if (!canvasRef.current) return;
|
||||
// // const ctx = canvasRef.current.getContext("2d");
|
||||
// // if (!ctx) return;
|
||||
|
||||
// // const chart = new Chart(ctx, {
|
||||
// // type,
|
||||
// // data: chartData,
|
||||
// // options: options,
|
||||
// // });
|
||||
|
||||
// // return () => chart.destroy();
|
||||
// // }, [chartData, type, title]);
|
||||
|
||||
// return <Bar data={measurements && measurements.length > 0 ? chartData : defaultData} options={options} />;
|
||||
// };
|
||||
|
||||
// export default LineGraphComponent;
|
||||
|
||||
|
||||
|
||||
import React, { useEffect, useMemo, useState } from "react";
|
||||
import { Bar } from "react-chartjs-2";
|
||||
import io from "socket.io-client";
|
||||
import { useThemeStore } from "../../../../store/useThemeStore";
|
||||
import useChartStore from "../../../../store/useChartStore";
|
||||
|
||||
interface ChartComponentProps {
|
||||
type: any;
|
||||
@@ -8,16 +197,42 @@ interface ChartComponentProps {
|
||||
fontFamily?: string;
|
||||
fontSize?: string;
|
||||
fontWeight?: "Light" | "Regular" | "Bold";
|
||||
data: any;
|
||||
}
|
||||
|
||||
const LineGraphComponent = ({
|
||||
const BarGraphComponent = ({
|
||||
type,
|
||||
title,
|
||||
fontFamily,
|
||||
fontSize,
|
||||
fontWeight = "Regular",
|
||||
}: ChartComponentProps) => {
|
||||
// Memoize Font Weight Mapping
|
||||
const { themeColor } = useThemeStore();
|
||||
const { measurements, duration } = useChartStore(); // Zustand Store
|
||||
const [chartData, setChartData] = useState<{ labels: string[]; datasets: any[] }>({
|
||||
labels: [],
|
||||
datasets: [],
|
||||
});
|
||||
|
||||
const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL;
|
||||
|
||||
const defaultData = {
|
||||
labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
|
||||
datasets: [
|
||||
{
|
||||
label: "Dataset",
|
||||
data: [12, 19, 3, 5, 2, 3],
|
||||
backgroundColor: ["#6f42c1"],
|
||||
borderColor: "#b392f0",
|
||||
borderWidth: 1,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
// Memoize Theme Colors
|
||||
const buttonActionColor = useMemo(() => themeColor[0] || "#5c87df", [themeColor]);
|
||||
const buttonAbortColor = useMemo(() => themeColor[1] || "#ffffff", [themeColor]);
|
||||
|
||||
// Memoize Font Styling
|
||||
const chartFontWeightMap = useMemo(
|
||||
() => ({
|
||||
Light: "lighter" as const,
|
||||
@@ -27,19 +242,9 @@ const LineGraphComponent = ({
|
||||
[]
|
||||
);
|
||||
|
||||
// Parse and Memoize Font Size
|
||||
const fontSizeValue = useMemo(
|
||||
() => (fontSize ? parseInt(fontSize) : 12),
|
||||
[fontSize]
|
||||
);
|
||||
const fontSizeValue = useMemo(() => (fontSize ? parseInt(fontSize) : 12), [fontSize]);
|
||||
const fontWeightValue = useMemo(() => chartFontWeightMap[fontWeight], [fontWeight, chartFontWeightMap]);
|
||||
|
||||
// Determine and Memoize Font Weight
|
||||
const fontWeightValue = useMemo(
|
||||
() => chartFontWeightMap[fontWeight],
|
||||
[fontWeight, chartFontWeightMap]
|
||||
);
|
||||
|
||||
// Memoize Chart Font Style
|
||||
const chartFontStyle = useMemo(
|
||||
() => ({
|
||||
family: fontFamily || "Arial",
|
||||
@@ -49,6 +254,7 @@ const LineGraphComponent = ({
|
||||
[fontFamily, fontSizeValue, fontWeightValue]
|
||||
);
|
||||
|
||||
// Memoized Chart Options
|
||||
const options = useMemo(
|
||||
() => ({
|
||||
responsive: true,
|
||||
@@ -66,7 +272,7 @@ const LineGraphComponent = ({
|
||||
scales: {
|
||||
x: {
|
||||
ticks: {
|
||||
display: false, // This hides the x-axis labels
|
||||
display: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -74,21 +280,53 @@ const LineGraphComponent = ({
|
||||
[title, chartFontStyle]
|
||||
);
|
||||
|
||||
const chartData = {
|
||||
labels: ["January", "February", "March", "April", "May", "June", "July"],
|
||||
datasets: [
|
||||
{
|
||||
label: "My First Dataset",
|
||||
data: [65, 59, 80, 81, 56, 55, 40],
|
||||
backgroundColor: "#6f42c1",
|
||||
borderColor: "#ffffff",
|
||||
borderWidth: 2,
|
||||
fill: false,
|
||||
},
|
||||
],
|
||||
};
|
||||
useEffect(() => {
|
||||
if (!iotApiUrl || !measurements || Object.keys(measurements).length === 0) return;
|
||||
|
||||
return <Bar data={chartData} options={options} />;
|
||||
const socket = io(`http://${iotApiUrl}`);
|
||||
|
||||
const inputData = {
|
||||
measurements,
|
||||
duration,
|
||||
interval: 1000,
|
||||
};
|
||||
|
||||
const startStream = () => {
|
||||
socket.emit("lineInput", inputData);
|
||||
};
|
||||
|
||||
socket.on("connect", startStream);
|
||||
|
||||
socket.on("lineOutput", (response) => {
|
||||
const responseData = response.data;
|
||||
console.log("Received data:", responseData);
|
||||
|
||||
// Extract timestamps and values
|
||||
const labels = responseData.time;
|
||||
const datasets = Object.keys(measurements).map((key) => {
|
||||
const measurement = measurements[key];
|
||||
const datasetKey = `${measurement.name}.${measurement.fields}`;
|
||||
return {
|
||||
label: datasetKey,
|
||||
data: responseData[datasetKey]?.values ?? [],
|
||||
backgroundColor: "#6f42c1",
|
||||
borderColor: "#b392f0",
|
||||
borderWidth: 1,
|
||||
};
|
||||
});
|
||||
|
||||
setChartData({ labels, datasets });
|
||||
});
|
||||
|
||||
return () => {
|
||||
socket.off("lineOutput");
|
||||
socket.emit("stop_stream"); // Stop streaming when component unmounts
|
||||
socket.disconnect();
|
||||
};
|
||||
}, [measurements, duration, iotApiUrl]);
|
||||
|
||||
return <Bar data={Object.keys(measurements).length > 0 ? chartData : defaultData} options={options} />;
|
||||
};
|
||||
|
||||
export default LineGraphComponent;
|
||||
export default BarGraphComponent;
|
||||
|
||||
|
||||
@@ -1,115 +1,15 @@
|
||||
// import { useMemo } from "react";
|
||||
// import { Line } from "react-chartjs-2";
|
||||
|
||||
// interface ChartComponentProps {
|
||||
// type: any;
|
||||
// title: string;
|
||||
// fontFamily?: string;
|
||||
// fontSize?: string;
|
||||
// fontWeight?: "Light" | "Regular" | "Bold";
|
||||
// data: any;
|
||||
// }
|
||||
|
||||
// const LineGraphComponent = ({
|
||||
// title,
|
||||
// fontFamily,
|
||||
// fontSize,
|
||||
// fontWeight = "Regular",
|
||||
// }: ChartComponentProps) => {
|
||||
// // Memoize Font Weight Mapping
|
||||
// const chartFontWeightMap = useMemo(
|
||||
// () => ({
|
||||
// Light: "lighter" as const,
|
||||
// Regular: "normal" as const,
|
||||
// Bold: "bold" as const,
|
||||
// }),
|
||||
// []
|
||||
// );
|
||||
|
||||
// // Parse and Memoize Font Size
|
||||
// const fontSizeValue = useMemo(
|
||||
// () => (fontSize ? parseInt(fontSize) : 12),
|
||||
// [fontSize]
|
||||
// );
|
||||
|
||||
// // Determine and Memoize Font Weight
|
||||
// const fontWeightValue = useMemo(
|
||||
// () => chartFontWeightMap[fontWeight],
|
||||
// [fontWeight, chartFontWeightMap]
|
||||
// );
|
||||
|
||||
// // Memoize Chart Font Style
|
||||
// const chartFontStyle = useMemo(
|
||||
// () => ({
|
||||
// family: fontFamily || "Arial",
|
||||
// size: fontSizeValue,
|
||||
// weight: fontWeightValue,
|
||||
// }),
|
||||
// [fontFamily, fontSizeValue, fontWeightValue]
|
||||
// );
|
||||
|
||||
// const options = useMemo(
|
||||
// () => ({
|
||||
// responsive: true,
|
||||
// maintainAspectRatio: false,
|
||||
// plugins: {
|
||||
// title: {
|
||||
// display: true,
|
||||
// text: title,
|
||||
// font: chartFontStyle,
|
||||
// },
|
||||
// legend: {
|
||||
// display: false,
|
||||
// },
|
||||
// },
|
||||
// scales: {
|
||||
// x: {
|
||||
// ticks: {
|
||||
// display: true, // This hides the x-axis labels
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// }),
|
||||
// [title, chartFontStyle]
|
||||
// );
|
||||
|
||||
// const chartData = {
|
||||
// labels: ["January", "February", "March", "April", "May", "June", "July"],
|
||||
// datasets: [
|
||||
// {
|
||||
// label: "My First Dataset",
|
||||
// data: [65, 59, 80, 81, 56, 55, 40],
|
||||
// backgroundColor: "#6f42c1", // Updated to #6f42c1 (Purple)
|
||||
// borderColor: "#ffffff", // Keeping border color white
|
||||
// borderWidth: 2,
|
||||
// fill: false,
|
||||
// },
|
||||
// ],
|
||||
// };
|
||||
|
||||
// return <Line data={chartData} options={options} />;
|
||||
// };
|
||||
|
||||
// export default LineGraphComponent;
|
||||
|
||||
|
||||
import React, { useEffect, useRef, useMemo, useState } from "react";
|
||||
import { Chart } from "chart.js/auto";
|
||||
import { useThemeStore } from "../../../../store/useThemeStore";
|
||||
import React, { useEffect, useMemo, useState } from "react";
|
||||
import { Line } from "react-chartjs-2";
|
||||
import io from "socket.io-client";
|
||||
import { Line } from 'react-chartjs-2';
|
||||
import { useThemeStore } from "../../../../store/useThemeStore";
|
||||
import useChartStore from "../../../../store/useChartStore";
|
||||
|
||||
// WebSocket Connection
|
||||
// const socket = io("http://localhost:5000"); // Adjust to your backend URL
|
||||
|
||||
interface ChartComponentProps {
|
||||
type: any;
|
||||
title: string;
|
||||
fontFamily?: string;
|
||||
fontSize?: string;
|
||||
fontWeight?: "Light" | "Regular" | "Bold";
|
||||
data: any;
|
||||
}
|
||||
|
||||
const LineGraphComponent = ({
|
||||
@@ -118,60 +18,55 @@ const LineGraphComponent = ({
|
||||
fontFamily,
|
||||
fontSize,
|
||||
fontWeight = "Regular",
|
||||
data,
|
||||
}: ChartComponentProps) => {
|
||||
const canvasRef = useRef<HTMLCanvasElement>(null);
|
||||
const { themeColor } = useThemeStore();
|
||||
const { measurements, duration } = useChartStore(); // Zustand Store
|
||||
const [chartData, setChartData] = useState<{ labels: string[]; datasets: any[] }>({
|
||||
labels: [],
|
||||
datasets: [],
|
||||
});
|
||||
|
||||
// Memoize Theme Colors to Prevent Unnecessary Recalculations
|
||||
const buttonActionColor = useMemo(
|
||||
() => themeColor[0] || "#5c87df",
|
||||
[themeColor]
|
||||
);
|
||||
const buttonAbortColor = useMemo(
|
||||
() => themeColor[1] || "#ffffff",
|
||||
[themeColor]
|
||||
);
|
||||
|
||||
// Memoize Font Weight Mapping
|
||||
const chartFontWeightMap = useMemo(
|
||||
() => ({
|
||||
Light: "lighter" as const,
|
||||
Regular: "normal" as const,
|
||||
Bold: "bold" as const,
|
||||
}),
|
||||
[]
|
||||
);
|
||||
|
||||
// Parse and Memoize Font Size
|
||||
const fontSizeValue = useMemo(
|
||||
() => (fontSize ? parseInt(fontSize) : 12),
|
||||
[fontSize]
|
||||
);
|
||||
|
||||
// Determine and Memoize Font Weight
|
||||
const fontWeightValue = useMemo(
|
||||
() => chartFontWeightMap[fontWeight],
|
||||
[fontWeight, chartFontWeightMap]
|
||||
);
|
||||
|
||||
// Memoize Chart Font Style
|
||||
const chartFontStyle = useMemo(
|
||||
() => ({
|
||||
family: fontFamily || "Arial",
|
||||
size: fontSizeValue,
|
||||
weight: fontWeightValue,
|
||||
}),
|
||||
[fontFamily, fontSizeValue, fontWeightValue]
|
||||
);
|
||||
|
||||
// Memoize Chart Data
|
||||
// const data = useMemo(() => propsData, [propsData]);
|
||||
|
||||
const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL;
|
||||
|
||||
const defaultData = {
|
||||
labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
|
||||
datasets: [
|
||||
{
|
||||
label: "Dataset",
|
||||
data: [12, 19, 3, 5, 2, 3],
|
||||
backgroundColor: ["#6f42c1"],
|
||||
borderColor: "#b392f0",
|
||||
borderWidth: 1,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
// Memoize Theme Colors
|
||||
const buttonActionColor = useMemo(() => themeColor[0] || "#5c87df", [themeColor]);
|
||||
const buttonAbortColor = useMemo(() => themeColor[1] || "#ffffff", [themeColor]);
|
||||
|
||||
// Memoize Font Styling
|
||||
const chartFontWeightMap = useMemo(
|
||||
() => ({
|
||||
Light: "lighter" as const,
|
||||
Regular: "normal" as const,
|
||||
Bold: "bold" as const,
|
||||
}),
|
||||
[]
|
||||
);
|
||||
|
||||
const fontSizeValue = useMemo(() => (fontSize ? parseInt(fontSize) : 12), [fontSize]);
|
||||
const fontWeightValue = useMemo(() => chartFontWeightMap[fontWeight], [fontWeight, chartFontWeightMap]);
|
||||
|
||||
const chartFontStyle = useMemo(
|
||||
() => ({
|
||||
family: fontFamily || "Arial",
|
||||
size: fontSizeValue,
|
||||
weight: fontWeightValue,
|
||||
}),
|
||||
[fontFamily, fontSizeValue, fontWeightValue]
|
||||
);
|
||||
|
||||
// Memoize Chart Options
|
||||
const options = useMemo(
|
||||
() => ({
|
||||
@@ -198,67 +93,54 @@ const LineGraphComponent = ({
|
||||
[title, chartFontStyle]
|
||||
);
|
||||
|
||||
const { measurements, setMeasurements, updateDuration, duration } = useChartStore();
|
||||
useEffect(() => {console.log(measurements);
|
||||
},[measurements])
|
||||
|
||||
useEffect(() => {
|
||||
if (!iotApiUrl || !measurements || Object.keys(measurements).length === 0) return;
|
||||
|
||||
const socket = io("http://192.168.0.192:5010");
|
||||
const socket = io(`http://${iotApiUrl}`);
|
||||
|
||||
if ( measurements.length > 0 ) {
|
||||
var inputes = {
|
||||
measurements: measurements,
|
||||
duration: duration,
|
||||
interval: 1000,
|
||||
}
|
||||
const inputData = {
|
||||
measurements,
|
||||
duration,
|
||||
interval: 1000,
|
||||
};
|
||||
|
||||
// Start stream
|
||||
const startStream = () => {
|
||||
socket.emit("lineInput", inputes);
|
||||
}
|
||||
socket.emit("lineInput", inputData);
|
||||
};
|
||||
|
||||
socket.on('connect', startStream);
|
||||
socket.on("connect", startStream);
|
||||
|
||||
socket.on("lineOutput", (response) => {
|
||||
const responceData = response.data;
|
||||
console.log("Received data:", responceData);
|
||||
const responseData = response.data;
|
||||
|
||||
// Extract timestamps and values
|
||||
const labels = responceData.time;
|
||||
const datasets = measurements.map((measurement: any) => {
|
||||
const key = `${measurement.name}.${measurement.fields}`;
|
||||
const labels = responseData.time;
|
||||
const datasets = Object.keys(measurements).map((key) => {
|
||||
const measurement = measurements[key];
|
||||
const datasetKey = `${measurement.name}.${measurement.fields}`;
|
||||
return {
|
||||
label: key,
|
||||
data: responceData[key]?.values ?? [], // Ensure it exists
|
||||
backgroundColor: themeColor[0] || "#5c87df",
|
||||
borderColor: themeColor[1] || "#ffffff",
|
||||
label: datasetKey,
|
||||
data: responseData[datasetKey]?.values ?? [],
|
||||
backgroundColor: "#6f42c1",
|
||||
borderColor: "#b392f0",
|
||||
borderWidth: 1,
|
||||
};
|
||||
});
|
||||
|
||||
setChartData({ labels, datasets });
|
||||
});
|
||||
}
|
||||
|
||||
return () => {
|
||||
socket.off("lineOutput");
|
||||
socket.emit("stop_stream"); // Stop streaming when component unmounts
|
||||
socket.disconnect();
|
||||
};
|
||||
}, [measurements, duration]);
|
||||
}, [measurements, duration, iotApiUrl]);
|
||||
|
||||
// useEffect(() => {
|
||||
// if (!canvasRef.current) return;
|
||||
// const ctx = canvasRef.current.getContext("2d");
|
||||
// if (!ctx) return;
|
||||
|
||||
// const chart = new Chart(ctx, {
|
||||
// type,
|
||||
// data: chartData,
|
||||
// options: options,
|
||||
// });
|
||||
|
||||
// return () => chart.destroy();
|
||||
// }, [chartData, type, title]);
|
||||
|
||||
return <Line data={chartData} options={options} />;
|
||||
return <Line data={Object.keys(measurements).length > 0 ? chartData : defaultData} options={options} />;
|
||||
};
|
||||
|
||||
export default LineGraphComponent;
|
||||
export default LineGraphComponent;
|
||||
|
||||
@@ -1,5 +1,195 @@
|
||||
import { useMemo } from "react";
|
||||
// import React, { useEffect, useRef, useMemo, useState } from "react";
|
||||
// import { Chart } from "chart.js/auto";
|
||||
// import { useThemeStore } from "../../../../store/useThemeStore";
|
||||
// import io from "socket.io-client";
|
||||
// import { Pie } from 'react-chartjs-2';
|
||||
// import useChartStore from "../../../../store/useChartStore";
|
||||
|
||||
// // WebSocket Connection
|
||||
// // const socket = io("http://localhost:5000"); // Adjust to your backend URL
|
||||
|
||||
// interface ChartComponentProps {
|
||||
// type: any;
|
||||
// title: string;
|
||||
// fontFamily?: string;
|
||||
// fontSize?: string;
|
||||
// fontWeight?: "Light" | "Regular" | "Bold";
|
||||
// data: any;
|
||||
// }
|
||||
|
||||
// const PieChartComponent = ({
|
||||
// type,
|
||||
// title,
|
||||
// fontFamily,
|
||||
// fontSize,
|
||||
// fontWeight = "Regular",
|
||||
// data,
|
||||
// }: ChartComponentProps) => {
|
||||
// const canvasRef = useRef<HTMLCanvasElement>(null);
|
||||
// const { themeColor } = useThemeStore();
|
||||
// const [chartData, setChartData] = useState<{ labels: string[]; datasets: any[] }>({
|
||||
// labels: [],
|
||||
// datasets: [],
|
||||
// });
|
||||
|
||||
// const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL;
|
||||
|
||||
// const defaultData = {
|
||||
// labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
|
||||
// datasets: [
|
||||
// {
|
||||
// label: "Dataset",
|
||||
// data: [12, 19, 3, 5, 2, 3],
|
||||
// backgroundColor: ["#6f42c1"],
|
||||
// borderColor: "#ffffff",
|
||||
// borderWidth: 2,
|
||||
// },
|
||||
// ],
|
||||
// };
|
||||
|
||||
// // Memoize Theme Colors to Prevent Unnecessary Recalculations
|
||||
// const buttonActionColor = useMemo(
|
||||
// () => themeColor[0] || "#6f42c1",
|
||||
// [themeColor]
|
||||
// );
|
||||
// const buttonAbortColor = useMemo(
|
||||
// () => themeColor[1] || "#ffffff",
|
||||
// [themeColor]
|
||||
// );
|
||||
|
||||
// // Memoize Font Weight Mapping
|
||||
// const chartFontWeightMap = useMemo(
|
||||
// () => ({
|
||||
// Light: "lighter" as const,
|
||||
// Regular: "normal" as const,
|
||||
// Bold: "bold" as const,
|
||||
// }),
|
||||
// []
|
||||
// );
|
||||
|
||||
// // Parse and Memoize Font Size
|
||||
// const fontSizeValue = useMemo(
|
||||
// () => (fontSize ? parseInt(fontSize) : 12),
|
||||
// [fontSize]
|
||||
// );
|
||||
|
||||
// // Determine and Memoize Font Weight
|
||||
// const fontWeightValue = useMemo(
|
||||
// () => chartFontWeightMap[fontWeight],
|
||||
// [fontWeight, chartFontWeightMap]
|
||||
// );
|
||||
|
||||
// // Memoize Chart Font Style
|
||||
// const chartFontStyle = useMemo(
|
||||
// () => ({
|
||||
// family: fontFamily || "Arial",
|
||||
// size: fontSizeValue,
|
||||
// weight: fontWeightValue,
|
||||
// }),
|
||||
// [fontFamily, fontSizeValue, fontWeightValue]
|
||||
// );
|
||||
|
||||
// // Memoize Chart Data
|
||||
// // const data = useMemo(() => propsData, [propsData]);
|
||||
|
||||
// // Memoize Chart Options
|
||||
// const options = useMemo(
|
||||
// () => ({
|
||||
// responsive: true,
|
||||
// maintainAspectRatio: false,
|
||||
// plugins: {
|
||||
// title: {
|
||||
// display: true,
|
||||
// text: title,
|
||||
// font: chartFontStyle,
|
||||
// },
|
||||
// legend: {
|
||||
// display: false,
|
||||
// },
|
||||
// },
|
||||
// scales: {
|
||||
// // x: {
|
||||
// // ticks: {
|
||||
// // display: true, // This hides the x-axis labels
|
||||
// // },
|
||||
// // },
|
||||
// },
|
||||
// }),
|
||||
// [title, chartFontStyle]
|
||||
// );
|
||||
|
||||
// const { measurements, setMeasurements, updateDuration, duration } = useChartStore();
|
||||
|
||||
// useEffect(() => {
|
||||
|
||||
// const socket = io(`http://${iotApiUrl}`);
|
||||
|
||||
// if ( measurements.length > 0 ) {
|
||||
// var inputes = {
|
||||
// measurements: measurements,
|
||||
// duration: duration,
|
||||
// interval: 1000,
|
||||
// }
|
||||
|
||||
// // Start stream
|
||||
// const startStream = () => {
|
||||
// socket.emit("lineInput", inputes);
|
||||
// }
|
||||
|
||||
// socket.on('connect', startStream);
|
||||
|
||||
// socket.on("lineOutput", (response) => {
|
||||
// const responceData = response.data;
|
||||
// console.log("Received data:", responceData);
|
||||
|
||||
// // Extract timestamps and values
|
||||
// const labels = responceData.time;
|
||||
// const datasets = measurements.map((measurement: any) => {
|
||||
// const key = `${measurement.name}.${measurement.fields}`;
|
||||
// return {
|
||||
// label: key,
|
||||
// data: responceData[key]?.values ?? [], // Ensure it exists
|
||||
// backgroundColor: "#6f42c1",
|
||||
// borderColor: "#ffffff",
|
||||
// };
|
||||
// });
|
||||
|
||||
// setChartData({ labels, datasets });
|
||||
// });
|
||||
// }
|
||||
|
||||
// return () => {
|
||||
// socket.off("lineOutput");
|
||||
// socket.emit("stop_stream"); // Stop streaming when component unmounts
|
||||
// };
|
||||
// }, [measurements, duration]);
|
||||
|
||||
// // useEffect(() => {
|
||||
// // if (!canvasRef.current) return;
|
||||
// // const ctx = canvasRef.current.getContext("2d");
|
||||
// // if (!ctx) return;
|
||||
|
||||
// // const chart = new Chart(ctx, {
|
||||
// // type,
|
||||
// // data: chartData,
|
||||
// // options: options,
|
||||
// // });
|
||||
|
||||
// // return () => chart.destroy();
|
||||
// // }, [chartData, type, title]);
|
||||
|
||||
// return <Pie data={measurements && measurements.length > 0 ? chartData : defaultData} options={options} />;
|
||||
// };
|
||||
|
||||
// export default PieChartComponent;
|
||||
|
||||
|
||||
|
||||
import React, { useEffect, useMemo, useState } from "react";
|
||||
import { Pie } from "react-chartjs-2";
|
||||
import io from "socket.io-client";
|
||||
import { useThemeStore } from "../../../../store/useThemeStore";
|
||||
import useChartStore from "../../../../store/useChartStore";
|
||||
|
||||
interface ChartComponentProps {
|
||||
type: any;
|
||||
@@ -7,16 +197,42 @@ interface ChartComponentProps {
|
||||
fontFamily?: string;
|
||||
fontSize?: string;
|
||||
fontWeight?: "Light" | "Regular" | "Bold";
|
||||
data: any;
|
||||
}
|
||||
|
||||
const PieChartComponent = ({
|
||||
type,
|
||||
title,
|
||||
fontFamily,
|
||||
fontSize,
|
||||
fontWeight = "Regular",
|
||||
}: ChartComponentProps) => {
|
||||
// Memoize Font Weight Mapping
|
||||
const { themeColor } = useThemeStore();
|
||||
const { measurements, duration } = useChartStore(); // Zustand Store
|
||||
const [chartData, setChartData] = useState<{ labels: string[]; datasets: any[] }>({
|
||||
labels: [],
|
||||
datasets: [],
|
||||
});
|
||||
|
||||
const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL;
|
||||
|
||||
const defaultData = {
|
||||
labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
|
||||
datasets: [
|
||||
{
|
||||
label: "Dataset",
|
||||
data: [12, 19, 3, 5, 2, 3],
|
||||
backgroundColor: ["#6f42c1"],
|
||||
borderColor: "#b392f0",
|
||||
borderWidth: 1,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
// Memoize Theme Colors
|
||||
const buttonActionColor = useMemo(() => themeColor[0] || "#5c87df", [themeColor]);
|
||||
const buttonAbortColor = useMemo(() => themeColor[1] || "#ffffff", [themeColor]);
|
||||
|
||||
// Memoize Font Styling
|
||||
const chartFontWeightMap = useMemo(
|
||||
() => ({
|
||||
Light: "lighter" as const,
|
||||
@@ -26,19 +242,9 @@ const PieChartComponent = ({
|
||||
[]
|
||||
);
|
||||
|
||||
// Parse and Memoize Font Size
|
||||
const fontSizeValue = useMemo(
|
||||
() => (fontSize ? parseInt(fontSize) : 12),
|
||||
[fontSize]
|
||||
);
|
||||
const fontSizeValue = useMemo(() => (fontSize ? parseInt(fontSize) : 12), [fontSize]);
|
||||
const fontWeightValue = useMemo(() => chartFontWeightMap[fontWeight], [fontWeight, chartFontWeightMap]);
|
||||
|
||||
// Determine and Memoize Font Weight
|
||||
const fontWeightValue = useMemo(
|
||||
() => chartFontWeightMap[fontWeight],
|
||||
[fontWeight, chartFontWeightMap]
|
||||
);
|
||||
|
||||
// Memoize Chart Font Style
|
||||
const chartFontStyle = useMemo(
|
||||
() => ({
|
||||
family: fontFamily || "Arial",
|
||||
@@ -48,12 +254,7 @@ const PieChartComponent = ({
|
||||
[fontFamily, fontSizeValue, fontWeightValue]
|
||||
);
|
||||
|
||||
// Access the CSS variable for the primary accent color
|
||||
const accentColor = getComputedStyle(document.documentElement)
|
||||
.getPropertyValue("--accent-color")
|
||||
.trim();
|
||||
|
||||
console.log("accentColor: ", accentColor);
|
||||
// Memoized Chart Options
|
||||
const options = useMemo(
|
||||
() => ({
|
||||
responsive: true,
|
||||
@@ -68,24 +269,64 @@ const PieChartComponent = ({
|
||||
display: false,
|
||||
},
|
||||
},
|
||||
scales: {
|
||||
// x: {
|
||||
// ticks: {
|
||||
// display: true,
|
||||
// },
|
||||
// },
|
||||
},
|
||||
}),
|
||||
[title, chartFontStyle]
|
||||
);
|
||||
|
||||
const chartData = {
|
||||
labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
|
||||
datasets: [
|
||||
{
|
||||
label: "Dataset",
|
||||
data: [12, 19, 3, 5, 2, 3],
|
||||
backgroundColor: ["#6f42c1"],
|
||||
borderColor: "#ffffff",
|
||||
borderWidth: 2,
|
||||
},
|
||||
],
|
||||
};
|
||||
useEffect(() => {
|
||||
if (!iotApiUrl || !measurements || Object.keys(measurements).length === 0) return;
|
||||
|
||||
return <Pie data={chartData} options={options} />;
|
||||
const socket = io(`http://${iotApiUrl}`);
|
||||
|
||||
const inputData = {
|
||||
measurements,
|
||||
duration,
|
||||
interval: 1000,
|
||||
};
|
||||
|
||||
const startStream = () => {
|
||||
socket.emit("lineInput", inputData);
|
||||
};
|
||||
|
||||
socket.on("connect", startStream);
|
||||
|
||||
socket.on("lineOutput", (response) => {
|
||||
const responseData = response.data;
|
||||
console.log("Received data:", responseData);
|
||||
|
||||
// Extract timestamps and values
|
||||
const labels = responseData.time;
|
||||
const datasets = Object.keys(measurements).map((key) => {
|
||||
const measurement = measurements[key];
|
||||
const datasetKey = `${measurement.name}.${measurement.fields}`;
|
||||
return {
|
||||
label: datasetKey,
|
||||
data: responseData[datasetKey]?.values ?? [],
|
||||
backgroundColor: "#6f42c1",
|
||||
borderColor: "#b392f0",
|
||||
borderWidth: 1,
|
||||
};
|
||||
});
|
||||
|
||||
setChartData({ labels, datasets });
|
||||
});
|
||||
|
||||
return () => {
|
||||
socket.off("lineOutput");
|
||||
socket.emit("stop_stream"); // Stop streaming when component unmounts
|
||||
socket.disconnect();
|
||||
};
|
||||
}, [measurements, duration, iotApiUrl]);
|
||||
|
||||
return <Pie data={Object.keys(measurements).length > 0 ? chartData : defaultData} options={options} />;
|
||||
};
|
||||
|
||||
export default PieChartComponent;
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ const ProgressCard = ({
|
||||
}) => (
|
||||
<div className="chart progressBar">
|
||||
<div className="header">{title}</div>
|
||||
{data?.stocks.map((stock, index) => (
|
||||
{data?.stocks?.map((stock, index) => (
|
||||
<div key={index} className="stock">
|
||||
<span className="stock-item">
|
||||
<span className="stockValues">
|
||||
|
||||
Reference in New Issue
Block a user