diff --git a/app/src/components/layout/sidebarLeft/visualization/Templates.tsx b/app/src/components/layout/sidebarLeft/visualization/Templates.tsx index b99b297..5eb8d4e 100644 --- a/app/src/components/layout/sidebarLeft/visualization/Templates.tsx +++ b/app/src/components/layout/sidebarLeft/visualization/Templates.tsx @@ -3,13 +3,10 @@ import { useDroppedObjectsStore } from "../../../../store/useDroppedObjectsStore import useTemplateStore from "../../../../store/useTemplateStore"; import { useSelectedZoneStore } from "../../../../store/useZoneStore"; import { getTemplateData } from "../../../../services/realTimeVisulization/zoneData/getTemplate"; -import { deleteTemplateApi } from "../../../../services/realTimeVisulization/zoneData/deleteTemplate"; -import { loadTempleteApi } from "../../../../services/realTimeVisulization/zoneData/loadTemplate"; import { useSocketStore } from "../../../../store/store"; const Templates = () => { - const { templates, removeTemplate } = useTemplateStore(); - const { setTemplates } = useTemplateStore(); + const { templates, removeTemplate, setTemplates } = useTemplateStore(); const { setSelectedZone, selectedZone } = useSelectedZoneStore(); const { visualizationSocket } = useSocketStore(); @@ -35,15 +32,11 @@ const Templates = () => { let deleteTemplate = { organization: organization, templateID: id, - } + }; if (visualizationSocket) { - visualizationSocket.emit("v2:viz-template:deleteTemplate", deleteTemplate) + visualizationSocket.emit("v2:viz-template:deleteTemplate", deleteTemplate); } removeTemplate(id); - // let response = await deleteTemplateApi(id, organization); - - // if (response.message === "Template deleted successfully") { - // } } catch (error) { console.error("Error deleting template:", error); } @@ -60,114 +53,54 @@ const Templates = () => { organization: organization, zoneId: selectedZone.zoneId, templateID: template.id, - } - console.log('template: ', template); - console.log('loadingTemplate: ', loadingTemplate); + }; if (visualizationSocket) { - visualizationSocket.emit("v2:viz-template:addToZone", loadingTemplate) + visualizationSocket.emit("v2:viz-template:addToZone", loadingTemplate); } - // let response = await loadTempleteApi(template.id, selectedZone.zoneId, organization); - // if (response.message === "Template placed in Zone") { - setSelectedZone({ - panelOrder: template.panelOrder, - activeSides: Array.from(new Set(template.panelOrder)), // No merging with previous `activeSides` - widgets: template.widgets, + setSelectedZone({ + panelOrder: template.panelOrder, + activeSides: Array.from(new Set(template.panelOrder)), // No merging with previous `activeSides` + widgets: template.widgets, + }); + + useDroppedObjectsStore.getState().setZone(selectedZone.zoneName, selectedZone.zoneId); + + if (Array.isArray(template.floatingWidget)) { + template.floatingWidget.forEach((val: any) => { + useDroppedObjectsStore.getState().addObject(selectedZone.zoneName, val); }); - - useDroppedObjectsStore.getState().setZone(selectedZone.zoneName, selectedZone.zoneId); - - if (Array.isArray(template.floatingWidget)) { - template.floatingWidget.forEach((val: any) => { - useDroppedObjectsStore.getState().addObject(selectedZone.zoneName, val); - }); - } - // } + } } catch (error) { console.error("Error loading template:", error); } }; - return ( -
+
{templates.map((template) => ( -
+
{template?.snapshot && ( -
- {" "} - {/* 16:9 aspect ratio */} +
{`${template.name} handleLoadTemplate(template)} />
)} -
+
handleLoadTemplate(template)} - style={{ - cursor: "pointer", - fontWeight: "500", - // ':hover': { - // textDecoration: 'underline' - // } - }} + className="template-name" > {template.name}
))} {templates.length === 0 && ( -
+
No saved templates yet. Create one in the visualization view!
)} @@ -192,4 +118,3 @@ const Templates = () => { }; export default Templates; - diff --git a/app/src/components/layout/sidebarLeft/visualization/widgets/WidgetsFloating.tsx b/app/src/components/layout/sidebarLeft/visualization/widgets/WidgetsFloating.tsx index 8c3df32..09c481a 100644 --- a/app/src/components/layout/sidebarLeft/visualization/widgets/WidgetsFloating.tsx +++ b/app/src/components/layout/sidebarLeft/visualization/widgets/WidgetsFloating.tsx @@ -46,7 +46,7 @@ const WidgetsFloating = () => { ))} */} {/* Floating 1 */} { const { addTemplate } = useTemplateStore(); const { selectedZone } = useSelectedZoneStore(); const { floatingWidget } = useFloatingWidget(); + const { widgets3D } = use3DWidget(); + const zones = useDroppedObjectsStore((state) => state.zones); + + // wall options const { toggleView, setToggleView } = useToggleView(); const { setDeleteTool } = useDeleteTool(); @@ -409,10 +414,9 @@ const Tools: React.FC = () => { widgets3D, selectedZone, templates, - visualizationSocket - }) - } - } + visualizationSocket, + }); + }} >
diff --git a/app/src/components/ui/componets/DisplayZone.tsx b/app/src/components/ui/componets/DisplayZone.tsx index 0890e5c..03fd44f 100644 --- a/app/src/components/ui/componets/DisplayZone.tsx +++ b/app/src/components/ui/componets/DisplayZone.tsx @@ -74,6 +74,7 @@ const DisplayZone: React.FC = ({ const [showLeftArrow, setShowLeftArrow] = useState(false); const [showRightArrow, setShowRightArrow] = useState(false); const { floatingWidget, setFloatingWidget } = useFloatingWidget() + const{setSelectedChartId}=useWidgetStore() // Function to calculate overflow state @@ -178,7 +179,7 @@ const DisplayZone: React.FC = ({ zoneViewPortPosition: response.viewPortposition || {}, }); } catch (error) { - console.error(error); + } } diff --git a/app/src/components/ui/componets/DraggableWidget.tsx b/app/src/components/ui/componets/DraggableWidget.tsx index fbb376f..cfd002d 100644 --- a/app/src/components/ui/componets/DraggableWidget.tsx +++ b/app/src/components/ui/componets/DraggableWidget.tsx @@ -18,6 +18,7 @@ import { deleteWidgetApi } from "../../../services/realTimeVisulization/zoneData import { useClickOutside } from "./functions/handleWidgetsOuterClick"; import { useSocketStore } from "../../../store/store"; import { usePlayButtonStore } from "../../../store/usePlayButtonStore"; +import OuterClick from "../../../utils/outerClick"; type Side = "top" | "bottom" | "left" | "right"; @@ -89,6 +90,11 @@ export const DraggableWidget = ({ const isPanelHidden = hiddenPanels.includes(widget.panel); + // OuterClick({ + // contextClassName: ["chart-container", "floating", "sidebar-right-wrapper"], + // setMenuVisible: () => setSelectedChartId(null), + // }); + const deleteSelectedChart = async () => { try { const email = localStorage.getItem("email") || ""; @@ -251,36 +257,35 @@ export const DraggableWidget = ({ }); // Track canvas dimensions - + // Current: Two identical useEffect hooks for canvas dimensions + // Remove the duplicate and keep only one + useEffect(() => { + const canvas = document.getElementById("real-time-vis-canvas"); + if (!canvas) return; -// Current: Two identical useEffect hooks for canvas dimensions -// Remove the duplicate and keep only one -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, + }); + }; - const updateCanvasDimensions = () => { - const rect = canvas.getBoundingClientRect(); - setCanvasDimensions({ - width: rect.width, - height: rect.height, - }); - }; + updateCanvasDimensions(); + const resizeObserver = new ResizeObserver(updateCanvasDimensions); + resizeObserver.observe(canvas); - updateCanvasDimensions(); - const resizeObserver = new ResizeObserver(updateCanvasDimensions); - resizeObserver.observe(canvas); - - return () => resizeObserver.unobserve(canvas); -}, []); + return () => resizeObserver.unobserve(canvas); + }, []); return ( <> @@ -296,13 +301,12 @@ useEffect(() => { onDragOver={handleDragOver} onDrop={handleDrop} style={{ - // Apply styles based on panel position width: ["top", "bottom"].includes(widget.panel) - ? `calc(${canvasDimensions.width * 0.16}px - 2px)` // For top/bottom panels, set width - : undefined, // Don't set width if it's left or right + ? `calc(${canvasDimensions.width}px / 6)` + : undefined, height: ["left", "right"].includes(widget.panel) - ? `calc(${canvasDimensions.height * 0.25}px - 2px)` // For left/right panels, set height - : undefined, // Don't set height if it's top or bottom + ? `calc(${canvasDimensions.height - 10}px / 4)` + : undefined, }} ref={chartWidget} onClick={() => setSelectedChartId(widget)} @@ -393,4 +397,4 @@ useEffect(() => { ); }; -// while resize calculate --realTimeViz-container-height pprperly \ No newline at end of file + diff --git a/app/src/components/ui/componets/DroppedFloatingWidgets.tsx b/app/src/components/ui/componets/DroppedFloatingWidgets.tsx index 4652ec1..bd707f4 100644 --- a/app/src/components/ui/componets/DroppedFloatingWidgets.tsx +++ b/app/src/components/ui/componets/DroppedFloatingWidgets.tsx @@ -126,12 +126,11 @@ const DroppedObjects: React.FC = () => { const email = localStorage.getItem("email") || ""; const organization = email?.split("@")[1]?.split(".")[0]; - let deleteFloatingWidget = { floatWidgetID: id, organization: organization, - zoneId: zone.zoneId - } + zoneId: zone.zoneId, + }; if (visualizationSocket) { visualizationSocket.emit("v2:viz-float:delete", deleteFloatingWidget); @@ -144,9 +143,7 @@ const DroppedObjects: React.FC = () => { // if (res.message === "FloatingWidget deleted successfully") { // deleteObject(zoneName, id, index); // Call the deleteObject method from the store // } - } catch (error) { - - } + } catch (error) {} } const handlePointerDown = (event: React.PointerEvent, index: number) => { @@ -461,15 +458,14 @@ const DroppedObjects: React.FC = () => { position: boundedPosition, }, index: draggingIndex.index, - zoneId: zone.zoneId - } + zoneId: zone.zoneId, + }; if (visualizationSocket) { visualizationSocket.emit("v2:viz-float:add", updateFloatingWidget); } // if (response.message === "Widget updated successfully") { - console.log('boundedPosition: ', boundedPosition); updateObjectPosition(zoneName, draggingIndex.index, boundedPosition); // } @@ -503,106 +499,130 @@ const DroppedObjects: React.FC = () => { setOpenKebabId((prevId) => (prevId === id ? null : id)); }; + const containerHeight = getComputedStyle( + document.documentElement + ).getPropertyValue("--realTimeViz-container-height"); + const containerWidth = getComputedStyle( + document.documentElement + ).getPropertyValue("--realTimeViz-container-width"); + + const heightMultiplier = parseFloat(containerHeight) * 0.14; + + const widthMultiplier = parseFloat(containerWidth) * 0.13; + + console.log("zone.objects: ", zone.objects); return (
- {zone.objects.map((obj, index) => ( -
{ - setSelectedChartId(obj); - handlePointerDown(event, index); - }} - > - {obj.className === "floating total-card" ? ( - <> - - - ) : obj.className === "warehouseThroughput floating" ? ( - <> - - - ) : obj.className === "fleetEfficiency floating" ? ( - <> - - - ) : null} + {zone.objects.map((obj, index) => { + const topPosition = + typeof obj.position.top === "number" + ? `calc(${obj.position.top}px + ${ + isPlaying && selectedZone.activeSides.includes("top") + ? `${heightMultiplier - 55}px` + : "0px" + })` + : "auto"; + + const leftPosition = + typeof obj.position.left === "number" + ? `calc(${obj.position.left}px + ${ + isPlaying && selectedZone.activeSides.includes("left") + ? `${widthMultiplier - 100}px` + : "0px" + })` + : "auto"; + + const rightPosition = + typeof obj.position.right === "number" + ? `calc(${obj.position.right}px + ${ + isPlaying && selectedZone.activeSides.includes("right") + ? `${widthMultiplier - 100}px` + : "0px" + })` + : "auto"; + + const bottomPosition = + typeof obj.position.bottom === "number" + ? `calc(${obj.position.bottom}px + ${ + isPlaying && selectedZone.activeSides.includes("bottom") + ? `${heightMultiplier - 55}px` + : "0px" + })` + : "auto"; + + return (
{ - event.stopPropagation(); - handleKebabClick(obj.id, event); + key={`${zoneName}-${index}`} + className={`${obj.className} ${ + selectedChartId?.id === obj.id && "activeChart" + }`} + ref={chartWidget} + style={{ + position: "absolute", + top: topPosition, + left: leftPosition, + right: rightPosition, + bottom: bottomPosition, + }} + onPointerDown={(event) => { + setSelectedChartId(obj); + handlePointerDown(event, index); }} > - -
- {openKebabId === obj.id && ( -
-
{ - event.stopPropagation(); - handleDuplicate(zoneName, index); // Call the duplicate handler - }} - > -
- -
-
Duplicate
-
-
{ - event.stopPropagation(); - handleDelete(zoneName, obj.id); // Call the delete handler - }} - > -
- -
-
Delete
-
+ {obj.className === "floating total-card" ? ( + + ) : obj.className === "warehouseThroughput floating" ? ( + + ) : obj.className === "fleetEfficiency floating" ? ( + + ) : null} + +
{ + event.stopPropagation(); + handleKebabClick(obj.id, event); + }} + > +
- )} -
- ))} + + {openKebabId === obj.id && ( +
+
{ + event.stopPropagation(); + handleDuplicate(zoneName, index); // Call the duplicate handler + }} + > +
+ +
+
Duplicate
+
+
{ + event.stopPropagation(); + handleDelete(zoneName, obj.id); // Call the delete handler + }} + > +
+ +
+
Delete
+
+
+ )} +
+ ); + })} {/* Render DistanceLines component during drag */} {isPlaying === false && diff --git a/app/src/components/ui/componets/Panel.tsx b/app/src/components/ui/componets/Panel.tsx index d24abcb..fe9836d 100644 --- a/app/src/components/ui/componets/Panel.tsx +++ b/app/src/components/ui/componets/Panel.tsx @@ -32,7 +32,6 @@ interface PanelProps { zoneName: string; activeSides: Side[]; panelOrder: Side[]; - lockedPanels: Side[]; zoneId: string; zoneViewPortTarget: number[]; @@ -41,7 +40,7 @@ interface PanelProps { }> >; hiddenPanels: string[]; - setZonesData: React.Dispatch>; // Add this line + setZonesData: React.Dispatch>; } const generateUniqueId = () => @@ -59,14 +58,13 @@ const Panel: React.FC = ({ [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"); @@ -80,42 +78,20 @@ const Panel: React.FC = ({ }); }; - // Initial measurement updateCanvasDimensions(); - - // Set up ResizeObserver to track changes const resizeObserver = new ResizeObserver(updateCanvasDimensions); resizeObserver.observe(canvas); - return () => { - resizeObserver.unobserve(canvas); - }; + return () => resizeObserver.unobserve(canvas); }, []); - 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, - }); - }; - - // Initial measurement - updateCanvasDimensions(); - - // Set up ResizeObserver to track changes - 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); @@ -125,123 +101,105 @@ const Panel: React.FC = ({ const topActive = previousPanels.includes("top"); const bottomActive = previousPanels.includes("bottom"); - // Dynamic panel sizes based on canvas width - const panelSizeWidth = isPlaying ? Math.max(canvasDimensions.width * 0.14, 200) : Math.max(canvasDimensions.width * 0.165, 200); // Ensure minimum width of 200px - const panelSizeHeight = isPlaying ? Math.max(canvasDimensions.width * 0.13, 200) : Math.max(canvasDimensions.width * 0.13, 200); // Ensure minimum height of 200px - switch (side) { case "top": case "bottom": return { - // minWidth: "200px", // Minimum width constraint - width: `calc(100% - ${(leftActive ? panelSizeWidth : 0) + - (rightActive ? panelSizeWidth : 0) - }px)`, - minHeight: "200px", // Minimum height constraint - height: `${panelSizeHeight - 2}px`, // Subtracting for border or margin - left: leftActive ? `${panelSizeWidth}px` : "0", - right: rightActive ? `${panelSizeWidth}px` : "0", + 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: "200px", // Minimum width constraint - width: `${panelSizeWidth - 2}px`, // Subtracting for border or margin - // minHeight: "200px", // Minimum height constraint - height: `calc(100% - ${(topActive ? panelSizeHeight : 0) + - (bottomActive ? panelSizeHeight : 0) - }px)`, - top: topActive ? `${panelSizeHeight}px` : "0", - bottom: bottomActive ? `${panelSizeHeight}px` : "0", + 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, - isPlaying, - canvasDimensions.width, - canvasDimensions.height, - ] + [selectedZone.panelOrder, panelSize] ); + // Handle drop event const handleDrop = (e: React.DragEvent, panel: Side) => { e.preventDefault(); const { draggedAsset } = useWidgetStore.getState(); - if (!draggedAsset) return; - if (isPanelLocked(panel)) return; + if (!draggedAsset || isPanelLocked(panel)) return; const currentWidgetsCount = getCurrentWidgetCount(panel); const maxCapacity = calculatePanelCapacity(panel); - if (currentWidgetsCount >= maxCapacity) return; - addWidgetToPanel(draggedAsset, 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 = 170; - const CHART_HEIGHT = 170; - const FALLBACK_HORIZONTAL_CAPACITY = 5; - const FALLBACK_VERTICAL_CAPACITY = 3; + const CHART_WIDTH = panelSize; + const CHART_HEIGHT = panelSize; const dimensions = panelDimensions[panel]; if (!dimensions) { - return panel === "top" || panel === "bottom" - ? FALLBACK_HORIZONTAL_CAPACITY - : FALLBACK_VERTICAL_CAPACITY; + return panel === "top" || panel === "bottom" ? 5 : 3; // Fallback capacities } return panel === "top" || panel === "bottom" - ? Math.floor(dimensions.width / CHART_WIDTH) - : Math.floor(dimensions.height / CHART_HEIGHT); + ? Math.max(1, Math.floor(dimensions.width / CHART_WIDTH)) + : Math.max(1, Math.floor(dimensions.height / CHART_HEIGHT)); }; - // while dublicate check this and add + // 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], })); - - try { - // let response = await addingWidgets(selectedZone.zoneId, organization, newWidget); - // if (response.message === "Widget created successfully") { - // setSelectedZone((prev) => ({ - // ...prev, - // widgets: [...prev.widgets, newWidget], - // })); - // } - } catch (error) { - console.error("Error adding widget:", error); - } }; + // Observe panel dimensions useEffect(() => { const observers: ResizeObserver[] = []; const currentPanelRefs = panelRefs.current; @@ -258,6 +216,7 @@ const Panel: React.FC = ({ })); } }); + observer.observe(element); observers.push(observer); } @@ -268,22 +227,15 @@ const Panel: React.FC = ({ }; }, [selectedZone.activeSides]); + // Handle widget reordering const handleReorder = (fromIndex: number, toIndex: number, panel: Side) => { - if (!selectedZone) return; // Ensure selectedZone is not null - setSelectedZone((prev) => { - if (!prev) return prev; // Ensure prev is not null - - // Filter widgets for the specified panel const widgetsInPanel = prev.widgets.filter((w) => w.panel === panel); - - // Reorder widgets within the same panel const reorderedWidgets = arrayMove(widgetsInPanel, fromIndex, toIndex); - // Merge the reordered widgets back into the full list while preserving the order const updatedWidgets = prev.widgets - .filter((widget) => widget.panel !== panel) // Keep widgets from other panels - .concat(reorderedWidgets); // Add the reordered widgets for the specified panel + .filter((widget) => widget.panel !== panel) + .concat(reorderedWidgets); return { ...prev, @@ -292,13 +244,42 @@ const Panel: React.FC = ({ }); }; + // Calculate capacities and dimensions + const topWidth = getPanelStyle("top").width; + const bottomWidth = getPanelStyle("bottom").width; + 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()} @@ -344,5 +325,3 @@ const Panel: React.FC = ({ }; export default Panel; - -// canvasDimensions.width as percent diff --git a/app/src/components/ui/realTimeVis/floating/SimpleCard.tsx b/app/src/components/ui/realTimeVis/floating/SimpleCard.tsx index 0c36977..7fafb79 100644 --- a/app/src/components/ui/realTimeVis/floating/SimpleCard.tsx +++ b/app/src/components/ui/realTimeVis/floating/SimpleCard.tsx @@ -5,7 +5,7 @@ interface SimpleCardProps { icon: React.ComponentType>; // React component for SVG icon value: string; per: string; // Percentage change - position?: [number, number] + position?: [number, number]; } const SimpleCard: React.FC = ({ @@ -15,7 +15,6 @@ const SimpleCard: React.FC = ({ per, position = [0, 0], }) => { - const handleDragStart = (event: React.DragEvent) => { const rect = event.currentTarget.getBoundingClientRect(); // Get position const cardData = JSON.stringify({ @@ -23,7 +22,7 @@ const SimpleCard: React.FC = ({ value, per, icon: Icon, - + className: event.currentTarget.className, position: [rect.top, rect.left], // ✅ Store position }); diff --git a/app/src/modules/visualization/handleSaveTemplate.ts b/app/src/modules/visualization/handleSaveTemplate.ts index e5f90ab..f192bec 100644 --- a/app/src/modules/visualization/handleSaveTemplate.ts +++ b/app/src/modules/visualization/handleSaveTemplate.ts @@ -64,7 +64,8 @@ export const handleSaveTemplate = async ({ floatingWidget, widgets3D, }; - + + console.log('newTemplate: ', newTemplate); // Extract organization from email const email = localStorage.getItem("email") || ""; const organization = email.includes("@") diff --git a/app/src/styles/layout/sidebar.scss b/app/src/styles/layout/sidebar.scss index 465af2c..3b14aac 100644 --- a/app/src/styles/layout/sidebar.scss +++ b/app/src/styles/layout/sidebar.scss @@ -70,6 +70,67 @@ position: relative; overflow: auto; + .template-list { + display: flex; + flex-direction: column; + gap: 1rem; + padding: 1rem; + min-height: 50vh; + max-height: 60vh; + } + + .template-item { + border: 1px solid #e0e0e0; + border-radius: 8px; + padding: 1rem; + transition: box-shadow 0.3s ease; + } + + .template-image-container { + position: relative; + padding-bottom: 56.25%; // 16:9 aspect ratio + } + + .template-image { + position: absolute; + width: 100%; + height: 100%; + object-fit: contain; + border-radius: 4px; + cursor: pointer; + transition: transform 0.3s ease; + } + + .template-details { + display: flex; + justify-content: space-between; + align-items: center; + margin-top: 0.5rem; + } + + .template-name { + cursor: pointer; + font-weight: 500; + } + + .delete-button { + padding: 0.25rem 0.5rem; + background: #ff4444; + color: white; + border: none; + border-radius: 4px; + cursor: pointer; + transition: opacity 0.3s ease; + } + + .no-templates { + text-align: center; + color: #666; + padding: 2rem; + grid-column: 1 / -1; + } + + .widget-left-sideBar { min-height: 50vh; max-height: 60vh; diff --git a/app/src/styles/pages/realTimeViz.scss b/app/src/styles/pages/realTimeViz.scss index cba4ede..0af1201 100644 --- a/app/src/styles/pages/realTimeViz.scss +++ b/app/src/styles/pages/realTimeViz.scss @@ -24,9 +24,17 @@ } .floating { - width: 100%; - max-width: 250px; - min-height: 83px; + + + width: calc(var(--realTimeViz-container-width) * 0.2); + height: calc(var(--realTimeViz-container-width) * 0.05); + + min-width: 250px; + max-width: 300px; + + min-height: 83px !important; + // max-height: 100px !important; + background: var(--background-color); border: 1.23px solid var(--border-color); box-shadow: 0px 4.91px 4.91px 0px #0000001c; @@ -60,9 +68,8 @@ display: flex; background-color: var(--background-color); position: absolute; - bottom: 10px; + // bottom: 10px; left: 50%; - transform: translate(-50%, 0); gap: 6px; border-radius: 8px; @@ -70,6 +77,7 @@ overflow: auto; max-width: calc(100% - 500px); z-index: 3; + transform: translate(-50%, -100%); &::-webkit-scrollbar { display: none; @@ -116,8 +124,8 @@ } .zone-wrapper.bottom { - bottom: calc(var(--realTimeViz-container-height) * 0.27); - bottom: 200px; + top: var(--bottomWidth); + // bottom: 200px; } .content-container { @@ -138,7 +146,7 @@ display: flex; background-color: rgba(224, 223, 255, 0.5); position: absolute; - bottom: 10px; + // bottom: 10px; left: 50%; transform: translate(-50%, 0); gap: 6px; @@ -203,9 +211,9 @@ .chart-container { width: 100%; - min-height: 150px; + max-height: 100%; - // border: 1px dashed var(--background-color-gray); + border: 1px dashed var(--background-color-gray); border-radius: 8px; box-shadow: var(--box-shadow-medium); padding: 6px 0; @@ -343,7 +351,7 @@ .chart-container { width: 100%; - min-height: 160px; + min-height: 150px; max-height: 100%; border-radius: 8px; box-shadow: var(--box-shadow-medium); @@ -362,7 +370,7 @@ .playingFlase { .zone-wrapper.bottom { - bottom: calc(var(--realTimeViz-container-height) * 0.25); + top: var(--bottomWidth); // bottom: 210px; } } @@ -786,4 +794,4 @@ } } } -} +} \ No newline at end of file diff --git a/app/src/utils/outerClick.ts b/app/src/utils/outerClick.ts index de8f7ef..280b3f0 100644 --- a/app/src/utils/outerClick.ts +++ b/app/src/utils/outerClick.ts @@ -1,7 +1,7 @@ import React from "react"; interface OuterClickProps { - contextClassName: string; + contextClassName: string[]; // Make sure this is an array of strings setMenuVisible: React.Dispatch>; } @@ -11,8 +11,12 @@ export default function OuterClick({ }: OuterClickProps) { const handleClick = (event: MouseEvent) => { const targets = event.target as HTMLElement; - // Check if the click is outside the selectable-dropdown-wrapper - if (!targets.closest(`.${contextClassName}`)) { + // Check if the click is outside of any of the provided class names + const isOutside = contextClassName.every( + (className) => !targets.closest(`.${className}`) + ); + + if (isOutside) { setMenuVisible(false); // Close the menu by updating the state } }; @@ -23,7 +27,7 @@ export default function OuterClick({ return () => { document.removeEventListener("click", handleClick); }; - }, []); + }, [contextClassName]); // Add contextClassName to dependency array to handle any changes return null; // This component doesn't render anything }