{selectedChartId?.title && (
@@ -80,50 +91,30 @@ const Data = () => {
{selectedChartId &&
chartDataGroups[selectedChartId.id]?.map((group) => (
- {/*
- •
- Data from
- {
- setChartDataGroups((prev) => ({
- ...prev,
- [selectedChartId.id]: prev[selectedChartId.id].map((g) =>
- g.id === group.id ? { ...g, easing } : g
- ),
- }));
- }}
- />
-
*/}
- {/* Render children only if there is a selected chart */}
- {group.children.map((child) => (
+ {group.children.map((child, index) => (
-
Input
+
Input {index + 1}
{
setChartDataGroups((prev) => ({
...prev,
- [selectedChartId.id]: prev[selectedChartId.id].map(
- (g) => ({
- ...g,
- children: g.children.map((c) =>
- c.id === child.id ? { ...c, easing } : c
- ),
- })
+ [selectedChartId.id]: prev[selectedChartId.id].map((g) =>
+ g.id === group.id
+ ? {
+ ...g,
+ children: g.children.map((c) =>
+ c.id === child.id ? { ...c, easing } : c
+ ),
+ }
+ : g
),
}));
}}
/>
- handleLinkClick(child.id)}>
-
+
handleAddClick(group.id)}>
+ +
{
{/* Chart Component */}
- {/* {selectedChartId && (
+ {selectedChartId && (
- )} */}
+ )}
{/* Options Container */}
diff --git a/frontend/src/components/ui/sideBar/realTimeViz/widgets.tsx b/frontend/src/components/ui/sideBar/realTimeViz/widgets.tsx
index 79746fc..2581ad7 100644
--- a/frontend/src/components/ui/sideBar/realTimeViz/widgets.tsx
+++ b/frontend/src/components/ui/sideBar/realTimeViz/widgets.tsx
@@ -61,136 +61,138 @@ const Widgets = () => {
- {/* Chart Widgets */}
-
- {chartTypes.map((type, index) => {
- const widgetTitle = `Widget ${index + 1}`;
- const sampleData = {
- labels: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul"],
- datasets: [
- {
- data: [65, 59, 80, 81, 56, 55, 40],
- backgroundColor: "#5c87df",
- borderColor: "#ffffff",
- borderWidth: 1,
- },
- ],
- };
+ {/* Chart Widgets */}.
+ {viewMode === "2D" && (
+
+ {chartTypes.map((type, index) => {
+ const widgetTitle = `Widget ${index + 1}`;
+ const sampleData = {
+ labels: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul"],
+ datasets: [
+ {
+ data: [65, 59, 80, 81, 56, 55, 40],
+ backgroundColor: "#5c87df",
+ borderColor: "#ffffff",
+ borderWidth: 1,
+ },
+ ],
+ };
- return (
-
{
- setDraggedAsset({
- type,
- id: `widget-${index + 1}`,
- title: widgetTitle,
- panel: "top", // Default panel assignment
- data: sampleData, // Include data in the dragged asset
- });
- }}
- onDragEnd={() => setDraggedAsset(null)}
- >
-
+ return (
+
{
+ setDraggedAsset({
+ type,
+ id: `widget-${index + 1}`,
+ title: widgetTitle,
+ panel: "top", // Default panel assignment
+ data: sampleData, // Include data in the dragged asset
+ });
+ }}
+ onDragEnd={() => setDraggedAsset(null)}
+ >
+
+
+ );
+ })}
+
{
+ setDraggedAsset({
+ type: "progress", // New widget type
+ id: `widget-7`,
+ title: "Widget 7",
+ panel: "top",
+ data: {
+ stocks: [
+ {
+ key: "units",
+ value: 1000,
+ description: "Initial stock",
+ },
+ ],
+ },
+ });
+ }}
+ onDragEnd={() => setDraggedAsset(null)}
+ >
+ {" "}
+
Widget 7
+
+
+
+ units
+ 1000
+
+ Initial stock
+
+
Icon
- );
- })}
-
{
- setDraggedAsset({
- type: "progress", // New widget type
- id: `widget-7`,
- title: "Widget 7",
- panel: "top",
- data: {
- stocks: [
- {
- key: "units",
- value: 1000,
- description: "Initial stock",
- },
- ],
- },
- });
- }}
- onDragEnd={() => setDraggedAsset(null)}
- >
- {" "}
-
Widget 7
-
+
{
+ setDraggedAsset({
+ type: "progress",
+ id: `widget-8`,
+ title: "Widget 8",
+ panel: "top",
+ data: {
+ stocks: [
+ {
+ key: "units",
+ value: 1000,
+ description: "Initial stock",
+ },
+ {
+ key: "units",
+ value: 500,
+ description: "Additional stock",
+ },
+ ],
+ },
+ });
+ }}
+ onDragEnd={() => setDraggedAsset(null)}
+ >
+ {" "}
+
Widget 8
+
+
+
+ units
+ 1000
+
+ Initial stock
-
Initial stock
-
-
Icon
+
Icon
+
+
+
+
+ units
+ 1000
+
+ Initial stock
+
+
Icon
+
-
{
- setDraggedAsset({
- type: "progress",
- id: `widget-8`,
- title: "Widget 8",
- panel: "top",
- data: {
- stocks: [
- {
- key: "units",
- value: 1000,
- description: "Initial stock",
- },
- {
- key: "units",
- value: 500,
- description: "Additional stock",
- },
- ],
- },
- });
- }}
- onDragEnd={() => setDraggedAsset(null)}
- >
- {" "}
-
Widget 8
-
-
-
- units
- 1000
-
- Initial stock
-
-
Icon
-
-
-
-
- units
- 1000
-
- Initial stock
-
-
Icon
-
-
-
+ )}
+ {viewMode === "3D" && <>>}
+ {viewMode === "Floating" && <>>}
);
};
export default Widgets;
-
-// along with my charts i need to additionallly drag and drop my 2 widget 7 and widget 8 styled card to pannel
diff --git a/frontend/src/functions/realTimeViz/deleteSelectedChart.tsx b/frontend/src/functions/realTimeViz/deleteSelectedChart.tsx
new file mode 100644
index 0000000..e69de29
diff --git a/frontend/src/pages/realTimeVisualization/addButtons.tsx b/frontend/src/pages/realTimeVisualization/addButtons.tsx
new file mode 100644
index 0000000..06f44eb
--- /dev/null
+++ b/frontend/src/pages/realTimeVisualization/addButtons.tsx
@@ -0,0 +1,187 @@
+import React from "react";
+import { CleanPannel, EyeIcon, LockIcon } from "../../assets/images/svgExports";
+
+// Define the type for `Side`
+type Side = "top" | "bottom" | "left" | "right";
+
+// Define the type for the props passed to the Buttons component
+interface ButtonsProps {
+ selectedZone: {
+ zoneName: string; // Add zoneName property
+ activeSides: Side[];
+ panelOrder: Side[];
+ lockedPanels: Side[];
+ widgets: {
+ id: string;
+ type: string;
+ title: string;
+ panel: Side;
+ data: any;
+ }[];
+ };
+ setSelectedZone: React.Dispatch<
+ React.SetStateAction<{
+ zoneName: string; // Ensure zoneName is included in the state type
+ activeSides: Side[];
+ panelOrder: Side[];
+ lockedPanels: Side[];
+ widgets: {
+ id: string;
+ type: string;
+ title: string;
+ panel: Side;
+ data: any;
+ }[];
+ }>
+ >;
+}
+
+const AddButtons: React.FC
= ({
+ selectedZone,
+ setSelectedZone,
+}) => {
+ // Function to toggle lock/unlock a panel
+ const toggleLockPanel = (side: Side) => {
+ const newLockedPanels = selectedZone.lockedPanels.includes(side)
+ ? selectedZone.lockedPanels.filter((panel) => panel !== side)
+ : [...selectedZone.lockedPanels, side];
+
+ const updatedZone = {
+ ...selectedZone,
+ lockedPanels: newLockedPanels,
+ };
+
+ // Update the selectedZone state
+ setSelectedZone(updatedZone);
+ };
+
+ // Function to toggle visibility of a panel
+ const toggleVisibility = (side: Side) => {
+ const newActiveSides = selectedZone.activeSides.includes(side)
+ ? selectedZone.activeSides.filter((s) => s !== side)
+ : [...selectedZone.activeSides, side];
+
+ const updatedZone = {
+ ...selectedZone,
+ activeSides: newActiveSides,
+ panelOrder: newActiveSides,
+ };
+
+ // Update the selectedZone state
+ setSelectedZone(updatedZone);
+ };
+
+ // Function to clean all widgets from a panel
+ const cleanPanel = (side: Side) => {
+ const cleanedWidgets = selectedZone.widgets.filter(
+ (widget) => widget.panel !== side
+ );
+
+ const updatedZone = {
+ ...selectedZone,
+ widgets: cleanedWidgets,
+ };
+
+ // Update the selectedZone state
+ setSelectedZone(updatedZone);
+ };
+
+ // Function to handle "+" button click
+ const handlePlusButtonClick = (side: Side) => {
+ if (selectedZone.activeSides.includes(side)) {
+ // If the panel is already active, remove all widgets and close the panel
+ const cleanedWidgets = selectedZone.widgets.filter(
+ (widget) => widget.panel !== side
+ );
+ const newActiveSides = selectedZone.activeSides.filter((s) => s !== side);
+
+ const updatedZone = {
+ ...selectedZone,
+ widgets: cleanedWidgets,
+ activeSides: newActiveSides,
+ panelOrder: newActiveSides,
+ };
+
+ // Update the selectedZone state
+ setSelectedZone(updatedZone);
+ } else {
+ // If the panel is not active, activate it
+ const newActiveSides = [...selectedZone.activeSides, side];
+
+ const updatedZone = {
+ ...selectedZone,
+ activeSides: newActiveSides,
+ panelOrder: newActiveSides,
+ };
+
+ // Update the selectedZone state
+ setSelectedZone(updatedZone);
+ }
+ };
+
+ return (
+
+ {(["top", "right", "bottom", "left"] as Side[]).map((side) => (
+
+ {/* "+" Button */}
+
+
+ {/* Extra Buttons */}
+
+ {/* Hide Panel */}
+
toggleVisibility(side)}
+ >
+
+
+
+ {/* Clean Panel */}
+
cleanPanel(side)}
+ >
+
+
+
+ {/* Lock/Unlock Panel */}
+
toggleLockPanel(side)}
+ >
+
+
+
+
+ ))}
+
+ );
+};
+
+export default AddButtons;
diff --git a/frontend/src/pages/realTimeVisualization/draggableWidget.tsx b/frontend/src/pages/realTimeVisualization/draggableWidget.tsx
new file mode 100644
index 0000000..6defa37
--- /dev/null
+++ b/frontend/src/pages/realTimeVisualization/draggableWidget.tsx
@@ -0,0 +1,240 @@
+import { useSortable } from "@dnd-kit/sortable";
+import ChartComponent from "../../components/ui/sideBar/realTimeViz/chartComponent";
+import { ProgressCard } from "./progressCard";
+import { useWidgetStore } from "../../store/store";
+import { useMemo, useState } from "react";
+
+export const DraggableWidget = ({ widget }: { widget: any }) => {
+ const {
+ attributes,
+ listeners,
+ setNodeRef,
+ transform,
+ transition,
+ isDragging,
+ } = useSortable({ id: widget.id });
+ const { selectedChartId, setSelectedChartId } = useWidgetStore();
+
+ // State for managing the popup visibility and customization options
+ const [isPopupOpen, setIsPopupOpen] = useState(false);
+ const [customizationOptions, setCustomizationOptions] = useState({
+ templateBackground: "#ffffff",
+ cardBackground: "#ffffff",
+ cardOpacity: 1,
+ cardBlur: 0,
+ font: "Arial",
+ margin: 0,
+ radius: 5,
+ shadow: "Low",
+ });
+
+ const style = useMemo(
+ () => ({
+ transform: transform
+ ? `translate3d(${transform.x}px, ${transform.y}px, 0)`
+ : undefined,
+ transition: transition || "transform 200ms ease",
+ }),
+ [transform, transition]
+ );
+
+ const handlePointerDown = () => {
+ if (!isDragging) {
+ setSelectedChartId(widget);
+ }
+ };
+
+ // Handle double-click to open the popup
+ const handleDoubleClick = () => {
+ setIsPopupOpen(true);
+ };
+
+ // Close the popup
+ const handleClosePopup = () => {
+ setIsPopupOpen(false);
+ };
+
+ // Save the changes made in the popup
+ const handleSaveChanges = () => {
+ // Here you can save the customizationOptions to your store or API
+ console.log("Saved Customization Options:", customizationOptions);
+ setIsPopupOpen(false);
+ };
+
+ // Compute dynamic card styles based on customizationOptions
+ const cardStyle = useMemo(() => {
+ const shadowLevels = {
+ Low: "0px 2px 4px rgba(0, 0, 0, 0.1)",
+ Medium: "0px 4px 8px rgba(0, 0, 0, 0.2)",
+ High: "0px 8px 16px rgba(0, 0, 0, 0.3)",
+ };
+
+ return {
+ backgroundColor: customizationOptions.cardBackground,
+ opacity: customizationOptions.cardOpacity,
+ filter: `blur(${customizationOptions.cardBlur}px)`,
+ fontFamily: customizationOptions.font,
+ margin: `${customizationOptions.margin}px`,
+ borderRadius: `${customizationOptions.radius}px`,
+ // boxShadow: shadowLevels[customizationOptions.shadow],
+ };
+ }, [customizationOptions]);
+
+ return (
+ <>
+
+ {widget.type === "progress" ? (
+
+ ) : (
+
+ )}
+
+
+ {/* Popup for Customizing Template Theme */}
+ {/* {isPopupOpen && (
+
+
+
Customize Template Theme
+
+
+
+ setCustomizationOptions((prev) => ({
+ ...prev,
+ templateBackground: e.target.value,
+ }))
+ }
+ />
+
+
+
+
+ setCustomizationOptions((prev) => ({
+ ...prev,
+ cardBackground: e.target.value,
+ }))
+ }
+ />
+
+
+
+
+ setCustomizationOptions((prev) => ({
+ ...prev,
+ cardOpacity: parseFloat(e.target.value),
+ }))
+ }
+ />
+
+
+
+
+ setCustomizationOptions((prev) => ({
+ ...prev,
+ cardBlur: parseInt(e.target.value),
+ }))
+ }
+ />
+
+
+
+
+
+
+
+
+ setCustomizationOptions((prev) => ({
+ ...prev,
+ margin: parseInt(e.target.value),
+ }))
+ }
+ />
+
+
+
+
+ setCustomizationOptions((prev) => ({
+ ...prev,
+ radius: parseInt(e.target.value),
+ }))
+ }
+ />
+
+
+
+
+
+
+
+
+
+
+
+ )} */}
+ >
+ );
+};
diff --git a/frontend/src/pages/realTimeVisualization/panel.tsx b/frontend/src/pages/realTimeVisualization/panel.tsx
new file mode 100644
index 0000000..a6faed5
--- /dev/null
+++ b/frontend/src/pages/realTimeVisualization/panel.tsx
@@ -0,0 +1,196 @@
+import React, { useEffect, useMemo, useRef, useState } from "react";
+import { DraggableWidget } from "./draggableWidget";
+import { useWidgetStore } from "../../store/store";
+
+type Side = "top" | "bottom" | "left" | "right";
+
+interface PanelProps {
+ selectedZone: {
+ zoneName: string; // Add zoneName property
+ activeSides: Side[];
+ panelOrder: Side[];
+ lockedPanels: Side[];
+ widgets: {
+ id: string;
+ type: string;
+ title: string;
+ panel: Side;
+ data: any;
+ }[];
+ };
+ setSelectedZone: React.Dispatch<
+ React.SetStateAction<{
+ zoneName: string; // Ensure zoneName is included in the state type
+ activeSides: Side[];
+ panelOrder: Side[];
+ lockedPanels: Side[];
+ widgets: {
+ id: string;
+ type: string;
+ title: string;
+ panel: Side;
+ data: any;
+ }[];
+ }>
+ >;
+}
+
+const generateUniqueId = () =>
+ `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
+
+const Panel: React.FC = ({ selectedZone, setSelectedZone }) => {
+ const panelRefs = useRef<{ [side in Side]?: HTMLDivElement }>({});
+ const [panelDimensions, setPanelDimensions] = useState<{
+ [side in Side]?: { width: number; height: number };
+ }>({});
+
+ const getPanelStyle = useMemo(
+ () => (side: Side) => {
+ const currentIndex = selectedZone.panelOrder.indexOf(side);
+ const previousPanels = selectedZone.panelOrder.slice(0, currentIndex);
+ const leftActive = previousPanels.includes("left");
+ const rightActive = previousPanels.includes("right");
+ const topActive = previousPanels.includes("top");
+ const bottomActive = previousPanels.includes("bottom");
+
+ switch (side) {
+ case "top":
+ case "bottom":
+ return {
+ width: `calc(100% - ${
+ (leftActive ? 204 : 0) + (rightActive ? 204 : 0)
+ }px)`,
+ left: leftActive ? "204px" : "0",
+ right: rightActive ? "204px" : "0",
+ [side]: "0",
+ height: "200px",
+ };
+ case "left":
+ case "right":
+ return {
+ height: `calc(100% - ${
+ (topActive ? 204 : 0) + (bottomActive ? 204 : 0)
+ }px)`,
+ top: topActive ? "204px" : "0",
+ bottom: bottomActive ? "204px" : "0",
+ [side]: "0",
+ width: "200px",
+ };
+ default:
+ return {};
+ }
+ },
+ [selectedZone.panelOrder]
+ );
+
+ const handleDrop = (e: React.DragEvent, panel: Side) => {
+ e.preventDefault();
+ const { draggedAsset } = useWidgetStore.getState();
+ if (draggedAsset) {
+ if (selectedZone.lockedPanels.includes(panel)) return;
+
+ const currentWidgetsInPanel = selectedZone.widgets.filter(
+ (w) => w.panel === panel
+ ).length;
+
+ const dimensions = panelDimensions[panel];
+ const CHART_WIDTH = 200; // Width of each chart for top/bottom panels
+ const CHART_HEIGHT = 200; // Height of each chart for left/right panels
+ let maxCharts = 0;
+
+ if (dimensions) {
+ if (panel === "top" || panel === "bottom") {
+ maxCharts = Math.floor(dimensions.width / CHART_WIDTH); // Use width for top/bottom
+ } else {
+ maxCharts = Math.floor(dimensions.height / CHART_HEIGHT); // Use height for left/right
+ }
+ } else {
+ maxCharts = panel === "top" || panel === "bottom" ? 5 : 3; // Default values
+ }
+
+ if (currentWidgetsInPanel >= maxCharts) {
+ return;
+ }
+
+ const updatedZone = {
+ ...selectedZone,
+ widgets: [
+ ...selectedZone.widgets,
+ {
+ ...draggedAsset,
+ id: generateUniqueId(),
+ panel,
+ },
+ ],
+ };
+
+ // Update the selectedZone state
+ setSelectedZone(updatedZone);
+ }
+ };
+
+ useEffect(() => {
+ const observers: ResizeObserver[] = [];
+ const currentPanelRefs = panelRefs.current;
+
+ selectedZone.activeSides.forEach((side) => {
+ const element = currentPanelRefs[side];
+ if (element) {
+ const observer = new ResizeObserver((entries) => {
+ for (const entry of entries) {
+ const { width, height } = entry.contentRect;
+ setPanelDimensions((prev) => ({
+ ...prev,
+ [side]: { width, height },
+ }));
+ }
+ });
+ observer.observe(element);
+ observers.push(observer);
+ }
+ });
+
+ return () => {
+ observers.forEach((observer) => observer.disconnect());
+ };
+ }, [selectedZone.activeSides]);
+
+ return (
+ <>
+ {selectedZone.activeSides.map((side) => (
+ handleDrop(e, side)}
+ onDragOver={(e) => e.preventDefault()}
+ ref={(el) => {
+ if (el) {
+ panelRefs.current[side] = el;
+ } else {
+ delete panelRefs.current[side];
+ }
+ }}
+ >
+
+ {selectedZone.widgets
+ .filter((w) => w.panel === side)
+ .map((widget) => (
+
+ ))}
+
+
+ ))}
+ >
+ );
+};
+
+export default Panel;
diff --git a/frontend/src/pages/realTimeVisualization/progressCard .tsx b/frontend/src/pages/realTimeVisualization/progressCard.tsx
similarity index 92%
rename from frontend/src/pages/realTimeVisualization/progressCard .tsx
rename to frontend/src/pages/realTimeVisualization/progressCard.tsx
index 8eb42ef..f7fc084 100644
--- a/frontend/src/pages/realTimeVisualization/progressCard .tsx
+++ b/frontend/src/pages/realTimeVisualization/progressCard.tsx
@@ -1,6 +1,9 @@
import React from "react";
-export const ProgressCard = ({ title, data }: {
+export const ProgressCard = ({
+ title,
+ data,
+}: {
title: string;
data: { stocks: Array<{ key: string; value: number; description: string }> };
}) => (
@@ -19,4 +22,4 @@ export const ProgressCard = ({ title, data }: {
))}
-);
\ No newline at end of file
+);
diff --git a/frontend/src/pages/realTimeVisualization/realTimeVisualization.tsx b/frontend/src/pages/realTimeVisualization/realTimeVisualization.tsx
index 268d3c1..559ede4 100644
--- a/frontend/src/pages/realTimeVisualization/realTimeVisualization.tsx
+++ b/frontend/src/pages/realTimeVisualization/realTimeVisualization.tsx
@@ -1,94 +1,14 @@
-import React, { useMemo, useState, useEffect, useRef } from "react";
-import {
- DndContext,
- closestCenter,
- KeyboardSensor,
- PointerSensor,
- useSensor,
- useSensors,
-} from "@dnd-kit/core";
-import {
- SortableContext,
- verticalListSortingStrategy,
- arrayMove,
- useSortable,
-} from "@dnd-kit/sortable";
-import { useWidgetStore } from "../../store/store";
-import ChartComponent from "../../components/ui/sideBar/realTimeViz/chartComponent";
+import { useState, useEffect } from "react";
+import { usePlayButtonStore, useWidgetStore } from "../../store/store";
import SideBar from "../../components/layout/sideBar";
-import {
- CleanPannel,
- DisableSorting,
- EyeIcon,
- LockIcon,
-} from "../../assets/images/svgExports";
-import { ProgressCard } from "./progressCard ";
+import Panel from "./panel";
+import AddButtons from "./addButtons";
+import { CommentIcon, PlayIcon } from "../../assets/images/svgExports";
type Side = "top" | "bottom" | "left" | "right";
-const generateUniqueId = () =>
- `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
-
-const DraggableWidget = ({ widget }: { widget: any }) => {
- const {
- attributes,
- listeners,
- setNodeRef,
- transform,
- transition,
- isDragging,
- } = useSortable({ id: widget.id });
- const { setSelectedChartId } = useWidgetStore();
-
- const style = useMemo(
- () => ({
- transform: transform
- ? `translate3d(${transform.x}px, ${transform.y}px, 0)`
- : undefined,
- transition: transition || "transform 200ms ease",
- }),
- [transform, transition]
- );
-
- const handlePointerDown = () => {
- if (!isDragging) {
- setSelectedChartId(widget);
- }
- };
-
- return (
-
- {widget.type === "progress" ? (
-
- ) : (
-
- )}
-
- );
-};
-
const RealTimeVisualization = () => {
- const [selectedZone, setSelectedZone] = useState("Manufacturing unit");
- const [panelDimensions, setPanelDimensions] = useState<{
- [side in Side]?: { width: number; height: number };
- }>({});
- const panelRefs = useRef<{ [side in Side]?: HTMLDivElement }>({});
-
+ // Dummy database for all zones
const [zonesData, setZonesData] = useState<{
[key: string]: {
activeSides: Side[];
@@ -135,352 +55,153 @@ const RealTimeVisualization = () => {
},
});
- useEffect(() => {}, [zonesData]);
+ // State to hold the currently selected zone's data, including the zone name
+ const [selectedZone, setSelectedZone] = useState<{
+ zoneName: string; // Add zoneName property
+ activeSides: Side[];
+ panelOrder: Side[];
+ lockedPanels: Side[];
+ widgets: {
+ id: string;
+ type: string;
+ title: string;
+ panel: Side;
+ data: any;
+ }[];
+ }>({
+ zoneName: "Manufacturing unit",
+ activeSides: [],
+ panelOrder: [],
+ lockedPanels: [],
+ widgets: [],
+ });
- const sensors = useSensors(
- useSensor(PointerSensor),
- useSensor(KeyboardSensor)
- );
+ useEffect(() => {}, [selectedZone]);
+
+ const { selectedChartId, setSelectedChartId } = useWidgetStore();
+
+ // Function to delete the selected chart
+ const deleteSelectedChart = () => {
+ if (!selectedChartId) {
+ return;
+ }
+ setZonesData((prev) => {
+ const updatedWidgets = selectedZone.widgets.filter(
+ (widget) => widget.id !== selectedChartId.id
+ );
+ return {
+ ...prev,
+ [selectedZone.zoneName]: {
+ ...selectedZone,
+ widgets: updatedWidgets,
+ },
+ };
+ });
+ setSelectedChartId(null);
+ };
+
+ // Handle keyboard events for delete functionality
+ useEffect(() => {
+ const handleKeyDown = (e: KeyboardEvent) => {
+ if ((e.key === "Delete" || e.key === "Backspace") && selectedChartId) {
+ deleteSelectedChart();
+ }
+ };
+ window.addEventListener("keydown", handleKeyDown);
+ return () => window.removeEventListener("keydown", handleKeyDown);
+ }, [selectedChartId]);
useEffect(() => {
- const observers: ResizeObserver[] = [];
- const currentPanelRefs = panelRefs.current;
+ setZonesData((prev) => ({
+ ...prev,
+ [selectedZone.zoneName]: selectedZone,
+ }));
+ }, [selectedZone]);
- zonesData[selectedZone].activeSides.forEach((side) => {
- const element = currentPanelRefs[side];
- if (element) {
- const observer = new ResizeObserver((entries) => {
- for (const entry of entries) {
- const { width, height } = entry.contentRect;
- setPanelDimensions((prev) => ({
- ...prev,
- [side]: { width, height },
- }));
- }
- });
- observer.observe(element);
- observers.push(observer);
- }
- });
+ const { isPlaying, setIsPlaying } = usePlayButtonStore(); // Access the store's state and setter function
- return () => {
- observers.forEach((observer) => observer.disconnect());
- };
- }, [zonesData[selectedZone].activeSides, selectedZone]);
-
- const toggleSide = (side: Side) => {
- setZonesData((prev) => {
- const zoneData = prev[selectedZone];
- const newActiveSides = zoneData.activeSides.includes(side)
- ? zoneData.activeSides.filter((s) => s !== side)
- : [...zoneData.activeSides, side];
- return {
- ...prev,
- [selectedZone]: {
- ...zoneData,
- activeSides: newActiveSides,
- panelOrder: newActiveSides,
- },
- };
- });
- };
-
- const toggleLockPanel = (side: Side) => {
- setZonesData((prev) => {
- const zoneData = prev[selectedZone];
- const newLockedPanels = zoneData.lockedPanels.includes(side)
- ? zoneData.lockedPanels.filter((panel) => panel !== side)
- : [...zoneData.lockedPanels, side];
- return {
- ...prev,
- [selectedZone]: {
- ...zoneData,
- lockedPanels: newLockedPanels,
- },
- };
- });
- };
-
- const getPanelStyle = useMemo(
- () => (side: Side) => {
- const currentIndex = zonesData[selectedZone].panelOrder.indexOf(side);
- const previousPanels = zonesData[selectedZone].panelOrder.slice(
- 0,
- currentIndex
- );
- const leftActive = previousPanels.includes("left");
- const rightActive = previousPanels.includes("right");
- const topActive = previousPanels.includes("top");
- const bottomActive = previousPanels.includes("bottom");
-
- switch (side) {
- case "top":
- case "bottom":
- return {
- width: `calc(100% - ${
- (leftActive ? 204 : 0) + (rightActive ? 204 : 0)
- }px)`,
- left: leftActive ? "204px" : "0",
- right: rightActive ? "204px" : "0",
- [side]: "0",
- height: "200px",
- };
- case "left":
- case "right":
- return {
- height: `calc(100% - ${
- (topActive ? 204 : 0) + (bottomActive ? 204 : 0)
- }px)`,
- top: topActive ? "204px" : "0",
- bottom: bottomActive ? "204px" : "0",
- [side]: "0",
- width: "200px",
- };
- default:
- return {};
- }
- },
- [zonesData, selectedZone]
- );
-
- const handleDrop = (e: React.DragEvent, panel: Side) => {
- e.preventDefault();
- const { draggedAsset } = useWidgetStore.getState();
- if (draggedAsset) {
- if (zonesData[selectedZone].lockedPanels.includes(panel)) return;
-
- const currentWidgetsInPanel = zonesData[selectedZone].widgets.filter(
- (w) => w.panel === panel
- ).length;
-
- const dimensions = panelDimensions[panel];
- const CHART_WIDTH = 200; // Width of each chart for top/bottom panels
- const CHART_HEIGHT = 200; // Height of each chart for left/right panels
- let maxCharts = 0;
-
- if (dimensions) {
- if (panel === "top" || panel === "bottom") {
- maxCharts = Math.floor(dimensions.width / CHART_WIDTH); // Use width for top/bottom
- } else {
- maxCharts = Math.floor(dimensions.height / CHART_HEIGHT); // Use height for left/right
- }
- } else {
- maxCharts = panel === "top" || panel === "bottom" ? 5 : 3; // Default values
- }
-
- if (currentWidgetsInPanel >= maxCharts) {
- return;
- }
-
- setZonesData((prev) => ({
- ...prev,
- [selectedZone]: {
- ...prev[selectedZone],
- widgets: [
- ...prev[selectedZone].widgets,
- {
- ...draggedAsset,
- id: generateUniqueId(),
- panel,
- },
- ],
- },
- }));
- }
- };
-
- const handleDragEnd = (event: any) => {
- const { active, over } = event;
- if (!over) return;
-
- setZonesData((prev) => {
- const zoneData = prev[selectedZone];
- const oldIndex = zoneData.widgets.findIndex(
- (widget) => widget.id === active.id
- );
- const newPanel =
- zoneData.widgets.find((widget) => widget.id === over.id)?.panel ||
- active.panel;
-
- if (active.panel === newPanel) {
- const newIndex = zoneData.widgets.findIndex(
- (widget) => widget.id === over.id
- );
- const reorderedWidgets = arrayMove(
- zoneData.widgets,
- oldIndex,
- newIndex
- );
- return {
- ...prev,
- [selectedZone]: {
- ...zoneData,
- widgets: reorderedWidgets,
- },
- };
- } else {
- const updatedWidgets = zoneData.widgets.map((widget) =>
- widget.id === active.id ? { ...widget, panel: newPanel } : widget
- );
- const widgetsInNewPanel = updatedWidgets.filter(
- (w) => w.panel === newPanel
- );
- const newIndex = widgetsInNewPanel.findIndex((w) => w.id === over.id);
- const reorderedWidgets = arrayMove(updatedWidgets, oldIndex, newIndex);
- return {
- ...prev,
- [selectedZone]: {
- ...zoneData,
- widgets: reorderedWidgets,
- },
- };
- }
- });
- };
+ const isZonesDataEmpty = Object.keys(zonesData).length === 0;
return (
-
-
-
-
+ <>
+ {zonesData ? (
+
+ {/* Sidebar for navigation */}
+ {!isPlaying && (
+
+ )}
+ {/* Main container */}
- {Object.keys(zonesData).map((zone, index) => (
-
setSelectedZone(zone)}
- >
- {zone}
-
- ))}
-
- {(["top", "right", "bottom", "left"] as Side[]).map((side) => (
-
-
-
-
toggleLockPanel(side)}
- >
-
-
-
toggleLockPanel(side)}
- >
-
-
-
toggleLockPanel(side)}
- >
-
-
-
toggleLockPanel(side)}
- >
-
-
-
-
- ))}
- {zonesData[selectedZone].activeSides.map((side) => (
handleDrop(e, side)}
- onDragOver={(e) => e.preventDefault()}
- ref={(el) => {
- if (el) {
- panelRefs.current[side] = el;
- } else {
- delete panelRefs.current[side];
- }
+ className="realTimeViz-tools"
+ style={{
+ top: !isPlaying ? "-20%" : "10px",
}}
>
+
+
+
setIsPlaying(!isPlaying)}
>
-
w.panel === side)
- .map((w) => w.id)}
- strategy={verticalListSortingStrategy}
- >
- {zonesData[selectedZone].widgets
- .filter((w) => w.panel === side)
- .map((widget) => (
-
- ))}
-
+
- ))}
+
+ {/* Display zones as selectable buttons */}
+ {Object.keys(zonesData).map((zoneName, index) => (
+
+ setSelectedZone({
+ zoneName,
+ ...zonesData[zoneName],
+ })
+ }
+ >
+ {zoneName}
+
+ ))}
+
+ {/* Add buttons component */}
+ {!isPlaying && (
+
+ )}
+ {/* Panel component */}
+
+
+ {/* Sidebar for data/design options */}
+ {!isPlaying && (
+
+ )}
-
-
-
+ ) : (
+ <>>
+ )}
+ >
);
};
-
export default RealTimeVisualization;
diff --git a/frontend/src/store/store.ts b/frontend/src/store/store.ts
index a33e535..84fdb1c 100644
--- a/frontend/src/store/store.ts
+++ b/frontend/src/store/store.ts
@@ -286,7 +286,7 @@ export const useThemeStore = create
((set) => ({
}));
// Define the WidgetStore interface
// Define the WidgetStore interface
-interface Widget {
+export interface Widget {
id: string;
type: string; // Can be chart type or "progress"
panel: "top" | "bottom" | "left" | "right";
@@ -315,7 +315,7 @@ interface Widget {
interface WidgetStore {
draggedAsset: Widget | null; // The currently dragged widget asset
widgets: Widget[]; // List of all widgets
- selectedChartId: Widget | null; // The currently selected chart/widget
+ selectedChartId: any;
setDraggedAsset: (asset: Widget | null) => void; // Setter for draggedAsset
addWidget: (widget: Widget) => void; // Add a new widget
setWidgets: (widgets: Widget[]) => void; // Replace the entire widgets array
@@ -333,3 +333,13 @@ export const useWidgetStore = create((set) => ({
setWidgets: (widgets) => set({ widgets }),
setSelectedChartId: (widget) => set({ selectedChartId: widget }),
}));
+
+type PlayButtonStore = {
+ isPlaying: boolean; // Updated state name to reflect the play/pause status more clearly
+ setIsPlaying: (value: boolean) => void; // Updated setter function name for clarity
+};
+
+export const usePlayButtonStore = create((set) => ({
+ isPlaying: false, // Default state for play/pause
+ setIsPlaying: (value) => set({ isPlaying: value }), // Update isPlaying state
+}));