import React, { useEffect, useMemo, useRef, useState } from "react"; import { arrayMove } from "@dnd-kit/sortable"; import { useAsset3dWidget, useSocketStore } from "../../../../store/store"; import { usePlayButtonStore } from "../../../../store/usePlayButtonStore"; import { useWidgetStore } from "../../../../store/useWidgetStore"; import { DraggableWidget } from "../2d/DraggableWidget"; type Side = "top" | "bottom" | "left" | "right"; interface Widget { id: string; type: string; title: string; panel: Side; data: any; } interface PanelProps { selectedZone: { zoneName: string; activeSides: Side[]; panelOrder: Side[]; lockedPanels: Side[]; points: []; zoneId: string; zoneViewPortTarget: number[]; zoneViewPortPosition: number[]; widgets: Widget[]; }; setSelectedZone: React.Dispatch< React.SetStateAction<{ zoneName: string; activeSides: Side[]; panelOrder: Side[]; lockedPanels: Side[]; points: []; zoneId: string; zoneViewPortTarget: number[]; zoneViewPortPosition: number[]; widgets: Widget[]; }> >; hiddenPanels: any; setZonesData: React.Dispatch>; waitingPanels: any; } const generateUniqueId = () => `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`; const Panel: React.FC = ({ selectedZone, setSelectedZone, hiddenPanels, setZonesData, waitingPanels, }) => { const { widgetSelect, setWidgetSelect } = useAsset3dWidget(); const panelRefs = useRef<{ [side in Side]?: HTMLDivElement }>({}); const [panelDimensions, setPanelDimensions] = useState<{ [side in Side]?: { width: number; height: number }; }>({}); const [openKebabId, setOpenKebabId] = useState(null); const { isPlaying } = usePlayButtonStore(); const { visualizationSocket } = useSocketStore(); const [canvasDimensions, setCanvasDimensions] = useState({ width: 0, height: 0, }); // Track canvas dimensions useEffect(() => { const canvas = document.getElementById("real-time-vis-canvas"); if (!canvas) return; const updateCanvasDimensions = () => { const rect = canvas.getBoundingClientRect(); setCanvasDimensions({ width: rect.width, height: rect.height, }); }; updateCanvasDimensions(); const resizeObserver = new ResizeObserver(updateCanvasDimensions); resizeObserver.observe(canvas); return () => resizeObserver.unobserve(canvas); }, []); // Calculate panel size const panelSize = Math.max( Math.min(canvasDimensions.width * 0.25, canvasDimensions.height * 0.25), 170 // Min 170px ); // Define getPanelStyle 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 { minWidth: "170px", width: `calc(100% - ${ (leftActive ? panelSize : 0) + (rightActive ? panelSize : 0) }px)`, minHeight: "170px", height: `${panelSize}px`, left: leftActive ? `${panelSize}px` : "0", right: rightActive ? `${panelSize}px` : "0", [side]: "0", }; case "left": case "right": return { minWidth: "170px", width: `${panelSize}px`, minHeight: "170px", height: `calc(100% - ${ (topActive ? panelSize : 0) + (bottomActive ? panelSize : 0) }px)`, top: topActive ? `${panelSize}px` : "0", bottom: bottomActive ? `${panelSize}px` : "0", [side]: "0", }; default: return {}; } }, [selectedZone.panelOrder, panelSize] ); // Handle drop event const handleDrop = (e: React.DragEvent, panel: Side) => { e.preventDefault(); const { draggedAsset } = useWidgetStore.getState(); if ( !draggedAsset || isPanelLocked(panel) || hiddenPanels[selectedZone.zoneId]?.includes(panel) ) return; const currentWidgetsCount = getCurrentWidgetCount(panel); const maxCapacity = calculatePanelCapacity(panel); if (currentWidgetsCount < maxCapacity) { addWidgetToPanel(draggedAsset, panel); } }; // Check if panel is locked const isPanelLocked = (panel: Side) => selectedZone.lockedPanels.includes(panel); // Get current widget count in a panel const getCurrentWidgetCount = (panel: Side) => selectedZone.widgets.filter((w) => w.panel === panel).length; // Calculate panel capacity const calculatePanelCapacity = (panel: Side) => { const CHART_WIDTH = panelSize - 10; const CHART_HEIGHT = panelSize - 10; const dimensions = panelDimensions[panel]; if (!dimensions) { return panel === "top" || panel === "bottom" ? 5 : 3; // Fallback capacities } return panel === "top" || panel === "bottom" ? Math.max(1, Math.floor(dimensions.width / CHART_WIDTH)) : Math.max(1, Math.floor(dimensions.height / CHART_HEIGHT)); }; // Add widget to panel 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, }; let addWidget = { organization: organization, zoneId: selectedZone.zoneId, widget: newWidget, }; if (visualizationSocket) { visualizationSocket.emit("v2:viz-widget:add", addWidget); } setSelectedZone((prev) => ({ ...prev, widgets: [...prev.widgets, newWidget], })); }; // Observe panel dimensions 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]); // Handle widget reordering const handleReorder = (fromIndex: number, toIndex: number, panel: Side) => { setSelectedZone((prev) => { const widgetsInPanel = prev.widgets.filter((w) => w.panel === panel); const reorderedWidgets = arrayMove(widgetsInPanel, fromIndex, toIndex); const updatedWidgets = prev.widgets .filter((widget) => widget.panel !== panel) .concat(reorderedWidgets); return { ...prev, widgets: updatedWidgets, }; }); }; // Calculate capacities and dimensions const topWidth = getPanelStyle("top").width; const bottomWidth = getPanelStyle("bottom").height; const leftHeight = getPanelStyle("left").height; const rightHeight = getPanelStyle("right").height; const topCapacity = calculatePanelCapacity("top"); const bottomCapacity = calculatePanelCapacity("bottom"); const leftCapacity = calculatePanelCapacity("left"); const rightCapacity = calculatePanelCapacity("right"); 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, index) => ( handleReorder(fromIndex, toIndex, side) } openKebabId={openKebabId} setOpenKebabId={setOpenKebabId} selectedZone={selectedZone} setSelectedZone={setSelectedZone} /> ))}
))} ); }; export default Panel;