diff --git a/app/package-lock.json b/app/package-lock.json index 45a89a1..937d3e6 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -11,6 +11,7 @@ "chart.js": "^4.4.8", "path": "^0.12.7", "react": "^19.0.0", + "react-chartjs-2": "^5.3.0", "react-dom": "^19.0.0", "react-router-dom": "^7.3.0", "zustand": "^5.0.3" @@ -3247,6 +3248,16 @@ "node": ">=0.10.0" } }, + "node_modules/react-chartjs-2": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/react-chartjs-2/-/react-chartjs-2-5.3.0.tgz", + "integrity": "sha512-UfZZFnDsERI3c3CZGxzvNJd02SHjaSJ8kgW1djn65H1KK8rehwTjyrRKOG3VTMG8wtHZ5rgAO5oTHtHi9GCCmw==", + "license": "MIT", + "peerDependencies": { + "chart.js": "^4.1.1", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/react-dom": { "version": "19.0.0", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.0.0.tgz", diff --git a/app/package.json b/app/package.json index abba5c4..3f3d446 100644 --- a/app/package.json +++ b/app/package.json @@ -13,6 +13,7 @@ "chart.js": "^4.4.8", "path": "^0.12.7", "react": "^19.0.0", + "react-chartjs-2": "^5.3.0", "react-dom": "^19.0.0", "react-router-dom": "^7.3.0", "zustand": "^5.0.3" diff --git a/app/src/components/layout/sidebarLeft/visualization/Templates.tsx b/app/src/components/layout/sidebarLeft/visualization/Templates.tsx index d54d666..4528c06 100644 --- a/app/src/components/layout/sidebarLeft/visualization/Templates.tsx +++ b/app/src/components/layout/sidebarLeft/visualization/Templates.tsx @@ -5,8 +5,8 @@ import { useSelectedZoneStore } from "../../../../store/useZoneStore"; const Templates = () => { const { templates, removeTemplate } = useTemplateStore(); const { setSelectedZone } = useSelectedZoneStore(); - - console.log('templates: ', templates); + + console.log("templates: ", templates); const handleDeleteTemplate = (id: string) => { removeTemplate(id); }; @@ -15,40 +15,49 @@ const Templates = () => { setSelectedZone((prev) => ({ ...prev, panelOrder: template.panelOrder, - activeSides: Array.from(new Set([...prev.activeSides, ...template.panelOrder])), + activeSides: Array.from( + new Set([...prev.activeSides, ...template.panelOrder]) + ), widgets: template.widgets, })); }; return ( -
+
{templates.map((template) => ( -
+
{template.snapshot && ( -
{/* 16:9 aspect ratio */} +
+ {" "} + {/* 16:9 aspect ratio */} {`${template.name} { />
)} -
+
handleLoadTemplate(template)} - style={{ - cursor: 'pointer', - fontWeight: '500', + style={{ + cursor: "pointer", + fontWeight: "500", // ':hover': { // textDecoration: 'underline' // } @@ -75,16 +86,16 @@ const Templates = () => { > {template.name}
-
))} {templates.length === 0 && ( -
+
No saved templates yet. Create one in the visualization view!
)} @@ -111,4 +124,3 @@ const Templates = () => { }; export default Templates; - diff --git a/app/src/components/layout/sidebarLeft/visualization/widgets/Widgets.tsx b/app/src/components/layout/sidebarLeft/visualization/widgets/Widgets.tsx index 3fde4c8..a96ff83 100644 --- a/app/src/components/layout/sidebarLeft/visualization/widgets/Widgets.tsx +++ b/app/src/components/layout/sidebarLeft/visualization/widgets/Widgets.tsx @@ -2,10 +2,10 @@ import { useState } from "react"; import ToggleHeader from "../../../../ui/inputs/ToggleHeader"; import Widgets2D from "./Widgets2D"; import Widgets3D from "./Widgets3D"; -import WidgetsTemplate from "./WidgetsTemplate"; +import WidgetsFloating from "./WidgetsFloating"; const Widgets = () => { - const [activeOption, setActiveOption] = useState("2D"); + const [activeOption, setActiveOption] = useState("Floating"); const handleToggleClick = (option: string) => { setActiveOption(option); @@ -14,13 +14,13 @@ const Widgets = () => { return (
{activeOption === "2D" && } {activeOption === "3D" && } - {activeOption === "Templates" && } + {activeOption === "Floating" && }
); }; diff --git a/app/src/components/layout/sidebarLeft/visualization/widgets/WidgetsFloating.tsx b/app/src/components/layout/sidebarLeft/visualization/widgets/WidgetsFloating.tsx new file mode 100644 index 0000000..9247508 --- /dev/null +++ b/app/src/components/layout/sidebarLeft/visualization/widgets/WidgetsFloating.tsx @@ -0,0 +1,191 @@ +import React, { + useState, + DragEvent, + MouseEvent, + useRef, + useEffect, +} from "react"; + +const WidgetsFloating: React.FC = () => { + const stateWorking = [ + { "Oil Tank": "24/341" }, + { "Oil Refin": "36.023" }, + { Transmission: "36.023" }, + { Fuel: "36732" }, + { Power: "1300" }, + { Time: "13-9-2023" }, + ]; + + // State for storing the dragged widget and its position + const [draggedFloating, setDraggedFloating] = useState(null); + const [position, setPosition] = useState<{ x: number; y: number }>({ + x: 0, + y: 0, + }); + + // State to store all placed widgets with their positions + const [placedWidgets, setPlacedWidgets] = useState< + { name: string; x: number; y: number }[] + >([]); + + const canvasRef = useRef(null); + + // Handle the drag start event + const handleDragStart = ( + event: DragEvent, + widget: string + ) => { + setDraggedFloating(widget); + // Initialize position to the current position when drag starts + setPosition({ x: event.clientX, y: event.clientY }); + }; + + // Handle the drag move event + const handleDragMove = (event: MouseEvent) => { + if (!draggedFloating) return; + + // Calculate new position and update it + const canvas = canvasRef.current; + if (canvas) { + const canvasRect = canvas.getBoundingClientRect(); + const newX = event.clientX - canvasRect.left; + const newY = event.clientY - canvasRect.top; + setPosition({ x: newX, y: newY }); + } + }; + + // Handle the drag end event + const handleDragEnd = () => { + if (draggedFloating) { + // Store the final position of the dragged widget + setPlacedWidgets((prevWidgets) => [ + ...prevWidgets, + { name: draggedFloating, x: position.x, y: position.y }, + ]); + // Reset the dragged floating widget after dragging is completed + setDraggedFloating(null); + } + }; + + useEffect(() => { + console.log("position: ", position); + console.log("draggedFloating: ", draggedFloating); + }, [draggedFloating, position]); + + return ( +
+ {/* The floating widget that's being dragged */} + {draggedFloating && ( +
+ {draggedFloating} +
+ )} + + {/* Render all placed widgets */} + {placedWidgets.map((widget, index) => ( +
+ {widget.name} +
+ ))} + + {/* The rest of your floating widgets */} +
handleDragStart(e, "working-state")} + style={{ position: "absolute", top: "50px", left: "50px" }} + > +
+
+
State
+
+ Working + +
+
+
+ Factory +
+
+
+ {stateWorking.map((state, index) => { + const key = Object.keys(state)[0]; + const value = state[key]; + return ( +
+ {key}: + {value} +
+ ); + })} +
+
+ + {/* Other floating widgets */} +
handleDragStart(e, "floating-2")} + style={{ position: "absolute", top: "120px", left: "150px" }} + > + floating-2 +
+
handleDragStart(e, "floating-3")} + style={{ position: "absolute", top: "200px", left: "250px" }} + > + floating-3 +
+
handleDragStart(e, "floating-4")} + style={{ position: "absolute", top: "300px", left: "350px" }} + > + floating-4 +
+
+ ); +}; + +export default WidgetsFloating; \ No newline at end of file diff --git a/app/src/components/layout/sidebarLeft/visualization/widgets/WidgetsTemplate.tsx b/app/src/components/layout/sidebarLeft/visualization/widgets/WidgetsTemplate.tsx deleted file mode 100644 index 14ab07e..0000000 --- a/app/src/components/layout/sidebarLeft/visualization/widgets/WidgetsTemplate.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import React from 'react' - -const WidgetsTemplate = () => { - return ( -
- WidgetsTemplate - -
- ) -} - -export default WidgetsTemplate diff --git a/app/src/components/layout/sidebarRight/SideBarRight.tsx b/app/src/components/layout/sidebarRight/SideBarRight.tsx index db44f69..5c59c19 100644 --- a/app/src/components/layout/sidebarRight/SideBarRight.tsx +++ b/app/src/components/layout/sidebarRight/SideBarRight.tsx @@ -65,7 +65,7 @@ const SideBarRight: React.FC = () => { )} {/* realtime visualization */} - {activeModule === "visualization" && } + {toggleUI && activeModule === "visualization" && }
); }; diff --git a/app/src/components/layout/sidebarRight/visualization/data/Data.tsx b/app/src/components/layout/sidebarRight/visualization/data/Data.tsx index d1122bb..e08c4d9 100644 --- a/app/src/components/layout/sidebarRight/visualization/data/Data.tsx +++ b/app/src/components/layout/sidebarRight/visualization/data/Data.tsx @@ -1,13 +1,9 @@ import React, { useEffect, useState } from "react"; import { useWidgetStore } from "../../../../../store/useWidgetStore"; -import { RemoveIcon } from "../../../../icons/ExportCommonIcons"; -import RegularDropDown from "../../../../ui/inputs/RegularDropDown"; +import { AddIcon, RemoveIcon } from "../../../../icons/ExportCommonIcons"; import MultiLevelDropDown from "../../../../ui/inputs/MultiLevelDropDown"; -interface Child { - id: number; - easing: string; -} +// Define the data structure for demonstration purposes const DATA_STRUCTURE = { furnace: { coolingRate: "coolingRate", @@ -35,13 +31,22 @@ const DATA_STRUCTURE = { data3: "Data 3", }, timestamp: { - data1: "Data 1", + data1: { + Data01: "Data 01", + Data02: "Data 02", + Data03: "Data 03", + }, data2: "Data 2", data3: "Data 3", }, }, }; +interface Child { + id: number; + easing: string; +} + interface Group { id: number; easing: string; @@ -65,11 +70,7 @@ const Data = () => { { id: Date.now(), easing: "Connecter 1", - children: [ - { id: Date.now(), easing: "Linear" }, - { id: Date.now() + 1, easing: "Ease Out" }, - { id: Date.now() + 2, easing: "Linear" }, - ], + children: [{ id: Date.now(), easing: "Linear" }], }, ], })); @@ -121,7 +122,43 @@ const Data = () => { {selectedChartId?.title && (
{selectedChartId?.title}
)} - {/* */} + {/* Render groups dynamically */} + {chartDataGroups[selectedChartId?.id]?.map((group) => ( +
+ {group.children.map((child, index) => ( +
+
Input {index + 1}
+
+ + {/* Add Icon */} + {group.children.length < 7 && ( +
handleAddClick(group.id)} // Pass groupId to handleAddClick + > + +
+ )} + {/* Remove Icon */} + + 1 ? "" : "disable" + }`} + onClick={(e) => { + e.stopPropagation(); // Prevent event bubbling + removeChild(group.id, child.id); // Pass groupId and childId to removeChild + }} + > + + +
+
+ ))} +
+ ))} + + {/* Info Box */}
i

diff --git a/app/src/components/ui/Tools.tsx b/app/src/components/ui/Tools.tsx index a182e1d..f6389e9 100644 --- a/app/src/components/ui/Tools.tsx +++ b/app/src/components/ui/Tools.tsx @@ -19,6 +19,7 @@ import useTemplateStore from "../../store/useTemplateStore"; import { useSelectedZoneStore } from "../../store/useZoneStore"; const Tools: React.FC = () => { + const { templates } = useTemplateStore(); const [activeTool, setActiveTool] = useState("cursor"); const [activeSubTool, setActiveSubTool] = useState("cursor"); const [toggleThreeD, setToggleThreeD] = useState(true); @@ -196,7 +197,13 @@ const Tools: React.FC = () => {

handleSaveTemplate({ addTemplate, selectedZone })} + onClick={() => + handleSaveTemplate({ + addTemplate, + selectedZone, + templates, + }) + } >
diff --git a/app/src/components/ui/charts/BarGraphComponent.tsx b/app/src/components/ui/charts/BarGraphComponent.tsx new file mode 100644 index 0000000..466d4df --- /dev/null +++ b/app/src/components/ui/charts/BarGraphComponent.tsx @@ -0,0 +1,94 @@ +import { useRef, useMemo } from "react"; + +import { Bar, 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: false, // 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", + borderColor: "#ffffff", + borderWidth: 2, + fill: false, + }, + ], + }; + + return ; +}; + +export default LineGraphComponent; diff --git a/app/src/components/ui/charts/LineGraphComponent.tsx b/app/src/components/ui/charts/LineGraphComponent.tsx new file mode 100644 index 0000000..98b9d61 --- /dev/null +++ b/app/src/components/ui/charts/LineGraphComponent.tsx @@ -0,0 +1,93 @@ +import { useRef, 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: false, // 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 ; +}; + +export default LineGraphComponent; diff --git a/app/src/components/ui/charts/PieGraphComponent.tsx b/app/src/components/ui/charts/PieGraphComponent.tsx new file mode 100644 index 0000000..fb553fb --- /dev/null +++ b/app/src/components/ui/charts/PieGraphComponent.tsx @@ -0,0 +1,90 @@ +import { useRef, useMemo } from "react"; +import { Pie } from "react-chartjs-2"; + +interface ChartComponentProps { + type: any; + title: string; + fontFamily?: string; + fontSize?: string; + fontWeight?: "Light" | "Regular" | "Bold"; + data: any; +} + +const PieChartComponent = ({ + 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] + ); + + // Access the CSS variable for the primary accent color + const accentColor = getComputedStyle(document.documentElement) + .getPropertyValue("--accent-color") + .trim(); + + const options = useMemo( + () => ({ + responsive: true, + maintainAspectRatio: false, + plugins: { + title: { + display: true, + text: title, + font: chartFontStyle, + }, + legend: { + display: false, + }, + }, + }), + [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, + }, + ], + }; + + return ; +}; + +export default PieChartComponent; diff --git a/app/src/components/ui/componets/AddButtons.tsx b/app/src/components/ui/componets/AddButtons.tsx index 0a810f7..4ff9ebb 100644 --- a/app/src/components/ui/componets/AddButtons.tsx +++ b/app/src/components/ui/componets/AddButtons.tsx @@ -11,7 +11,7 @@ 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 + zoneName: string; activeSides: Side[]; panelOrder: Side[]; lockedPanels: Side[]; @@ -25,7 +25,7 @@ interface ButtonsProps { }; setSelectedZone: React.Dispatch< React.SetStateAction<{ - zoneName: string; // Ensure zoneName is included in the state type + zoneName: string; activeSides: Side[]; panelOrder: Side[]; lockedPanels: Side[]; @@ -38,12 +38,18 @@ interface ButtonsProps { }[]; }> >; + hiddenPanels: Side[]; // Add this prop for hidden panels + setHiddenPanels: React.Dispatch>; // Add this prop for updating hidden panels } const AddButtons: React.FC = ({ selectedZone, setSelectedZone, + setHiddenPanels, + hiddenPanels, }) => { + // Local state to track hidden panels + // Function to toggle lock/unlock a panel const toggleLockPanel = (side: Side) => { const newLockedPanels = selectedZone.lockedPanels.includes(side) @@ -61,18 +67,14 @@ const AddButtons: React.FC = ({ // 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); + const isHidden = hiddenPanels.includes(side); + if (isHidden) { + // If the panel is already hidden, remove it from the hiddenPanels array + setHiddenPanels(hiddenPanels.filter((panel) => panel !== side)); + } else { + // If the panel is visible, add it to the hiddenPanels array + setHiddenPanels([...hiddenPanels, side]); + } }; // Function to clean all widgets from a panel @@ -145,8 +147,12 @@ const AddButtons: React.FC = ({
{/* Hide Panel */}
toggleVisibility(side)} > diff --git a/app/src/components/ui/componets/DisplayZone.tsx b/app/src/components/ui/componets/DisplayZone.tsx new file mode 100644 index 0000000..b5fe0f0 --- /dev/null +++ b/app/src/components/ui/componets/DisplayZone.tsx @@ -0,0 +1,178 @@ +import React, { useEffect, useRef } from "react"; +import { useSelectedZoneStore } from "../../../store/useZoneStore"; +import { Widget } from "../../../store/useWidgetStore"; + +// Define the type for `Side` +type Side = "top" | "bottom" | "left" | "right"; + +interface DisplayZoneProps { + zonesData: { + [key: string]: { + activeSides: Side[]; + panelOrder: Side[]; + lockedPanels: Side[]; + widgets: Widget[]; + }; + }; + selectedZone: { + zoneName: string; + activeSides: Side[]; + panelOrder: Side[]; + lockedPanels: Side[]; + widgets: { + id: string; + type: string; + title: string; + panel: Side; + data: any; + }[]; + }; + setSelectedZone: React.Dispatch< + React.SetStateAction<{ + zoneName: string; + activeSides: Side[]; + panelOrder: Side[]; + lockedPanels: Side[]; + widgets: { + id: string; + type: string; + title: string; + panel: Side; + data: any; + }[]; + }> + >; +} + +const DisplayZone: React.FC = ({ + zonesData, + selectedZone, + setSelectedZone, +}) => { + // Ref for the container element + const containerRef = useRef(null); + + // Example state for selectedOption and options (adjust based on your actual use case) + const [selectedOption, setSelectedOption] = React.useState( + null + ); + const [options, setOptions] = React.useState([]); + + // Scroll to the selected option when it changes + useEffect(() => { + const container = containerRef.current; + + if (container && selectedOption) { + // Handle scrolling to the selected option + const index = options.findIndex((option) => { + const formattedOption = formatOptionName(option); + const selectedFormattedOption = + selectedOption?.split("_")[1] || selectedOption; + return formattedOption === selectedFormattedOption; + }); + + if (index !== -1) { + const optionElement = container.children[index] as HTMLElement; + if (optionElement) { + optionElement.scrollIntoView({ + behavior: "smooth", + block: "nearest", + inline: "center", + }); + } + } + } + }, [selectedOption, options]); + + useEffect(() => { + const container = containerRef.current; + + const handleWheel = (event: WheelEvent) => { + event.preventDefault(); + if (container) { + container.scrollBy({ + left: event.deltaY * 2, // Adjust the multiplier for faster scrolling + behavior: "smooth", + }); + } + }; + + let isDragging = false; + let startX: number; + let scrollLeft: number; + + const handleMouseDown = (event: MouseEvent) => { + isDragging = true; + startX = event.pageX - (container?.offsetLeft || 0); + scrollLeft = container?.scrollLeft || 0; + }; + + const handleMouseMove = (event: MouseEvent) => { + if (!isDragging || !container) return; + event.preventDefault(); + const x = event.pageX - (container.offsetLeft || 0); + const walk = (x - startX) * 2; // Adjust the multiplier for faster dragging + container.scrollLeft = scrollLeft - walk; + }; + + const handleMouseUp = () => { + isDragging = false; + }; + + const handleMouseLeave = () => { + isDragging = false; + }; + + if (container) { + container.addEventListener("wheel", handleWheel, { passive: false }); + container.addEventListener("mousedown", handleMouseDown); + container.addEventListener("mousemove", handleMouseMove); + container.addEventListener("mouseup", handleMouseUp); + container.addEventListener("mouseleave", handleMouseLeave); + } + + return () => { + if (container) { + container.removeEventListener("wheel", handleWheel); + container.removeEventListener("mousedown", handleMouseDown); + container.removeEventListener("mousemove", handleMouseMove); + container.removeEventListener("mouseup", handleMouseUp); + container.removeEventListener("mouseleave", handleMouseLeave); + } + }; + }, []); + + // Helper function to format option names (customize as needed) + const formatOptionName = (option: string): string => { + // Replace underscores with spaces and capitalize the first letter + return option.replace(/_/g, " ").replace(/^\w/, (c) => c.toUpperCase()); + }; + + return ( +
+ {Object.keys(zonesData).map((zoneName, index) => ( +
{ + setSelectedZone({ + zoneName, + ...zonesData[zoneName], + }); + }} + > + {zoneName} +
+ ))} +
+ ); +}; + +export default DisplayZone; \ No newline at end of file diff --git a/app/src/components/ui/componets/DraggableWidget.tsx b/app/src/components/ui/componets/DraggableWidget.tsx index 342f31f..6769890 100644 --- a/app/src/components/ui/componets/DraggableWidget.tsx +++ b/app/src/components/ui/componets/DraggableWidget.tsx @@ -1,217 +1,84 @@ import { useMemo, useState } from "react"; import ChartComponent from "../../layout/sidebarLeft/visualization/widgets/ChartComponent"; import { useWidgetStore } from "../../../store/useWidgetStore"; +import PieGraphComponent from "../charts/PieGraphComponent"; +import BarGraphComponent from "../charts/BarGraphComponent"; +import LineGraphComponent from "../charts/LineGraphComponent"; export const DraggableWidget = ({ widget }: { widget: any }) => { 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", - }); - - // Handle pointer down to select the chart const handlePointerDown = () => { if (selectedChartId?.id !== widget.id) { 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" ? ( - // - <> + // + <> ) : ( - + <> + {widget.type === "line" && ( + + )} + {widget.type === "bar" && ( + + )} + {widget.type === "pie" && ( + + )} + )}
- - {/* 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/app/src/components/ui/componets/Panel.tsx b/app/src/components/ui/componets/Panel.tsx index 502114b..4a1305f 100644 --- a/app/src/components/ui/componets/Panel.tsx +++ b/app/src/components/ui/componets/Panel.tsx @@ -83,47 +83,54 @@ const Panel: React.FC = ({ selectedZone, setSelectedZone }) => { 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; - const CHART_HEIGHT = 200; - let maxCharts = 0; - - if (dimensions) { - if (panel === "top" || panel === "bottom") { - maxCharts = Math.floor(dimensions.width / CHART_WIDTH); - } else { - maxCharts = Math.floor(dimensions.height / CHART_HEIGHT); - } - } else { - maxCharts = panel === "top" || panel === "bottom" ? 5 : 3; - } - - if (currentWidgetsInPanel >= maxCharts) { - return; - } - - const updatedZone = { - ...selectedZone, - widgets: [ - ...selectedZone.widgets, - { - ...draggedAsset, - id: generateUniqueId(), - panel, - }, - ], - }; - - setSelectedZone(updatedZone); + + if (!draggedAsset) return; + if (isPanelLocked(panel)) return; + + const currentWidgetsCount = getCurrentWidgetCount(panel); + const maxCapacity = calculatePanelCapacity(panel); + + if (currentWidgetsCount >= maxCapacity) return; + + addWidgetToPanel(draggedAsset, panel); + }; + + // Helper functions + const isPanelLocked = (panel: Side) => + selectedZone.lockedPanels.includes(panel); + + const getCurrentWidgetCount = (panel: Side) => + selectedZone.widgets.filter(w => w.panel === panel).length; + + const calculatePanelCapacity = (panel: Side) => { + const CHART_WIDTH = 200; + const CHART_HEIGHT = 200; + const FALLBACK_HORIZONTAL_CAPACITY = 5; + const FALLBACK_VERTICAL_CAPACITY = 3; + + const dimensions = panelDimensions[panel]; + if (!dimensions) { + return panel === "top" || panel === "bottom" + ? FALLBACK_HORIZONTAL_CAPACITY + : FALLBACK_VERTICAL_CAPACITY; } + + return panel === "top" || panel === "bottom" + ? Math.floor(dimensions.width / CHART_WIDTH) + : Math.floor(dimensions.height / CHART_HEIGHT); + }; + + const addWidgetToPanel = (asset: any, panel: Side) => { + const newWidget = { + ...asset, + id: generateUniqueId(), + panel, + }; + + setSelectedZone(prev => ({ + ...prev, + widgets: [...prev.widgets, newWidget] + })); }; useEffect(() => { @@ -172,7 +179,7 @@ const Panel: React.FC = ({ selectedZone, setSelectedZone }) => { }} >
= ({ selectedZone, setSelectedZone }) => { opacity: selectedZone.lockedPanels.includes(side) ? "0.8" : "1", }} > + <>{} {selectedZone.widgets .filter((w) => w.panel === side) .map((widget) => ( @@ -193,5 +201,3 @@ const Panel: React.FC = ({ selectedZone, setSelectedZone }) => { }; export default Panel; -// only load selected template - diff --git a/app/src/components/ui/componets/RealTimeVisulization.tsx b/app/src/components/ui/componets/RealTimeVisulization.tsx index fb2d653..6daa075 100644 --- a/app/src/components/ui/componets/RealTimeVisulization.tsx +++ b/app/src/components/ui/componets/RealTimeVisulization.tsx @@ -3,6 +3,7 @@ import { usePlayButtonStore } from "../../../store/usePlayButtonStore"; import Panel from "./Panel"; import AddButtons from "./AddButtons"; import { useSelectedZoneStore } from "../../../store/useZoneStore"; +import DisplayZone from "./DisplayZone"; type Side = "top" | "bottom" | "left" | "right"; @@ -15,6 +16,7 @@ interface Widget { } const RealTimeVisulization: React.FC = () => { + const [hiddenPanels, setHiddenPanels] = React.useState([]); const containerRef = useRef(null); const [zonesData, setZonesData] = useState<{ [key: string]: { @@ -74,34 +76,19 @@ const RealTimeVisulization: React.FC = () => { style={{ height: isPlaying ? "100vh" : "", width: isPlaying ? "100%" : "", - left: isPlaying ? "50%" : "", + left: isPlaying ? "0%" : "", }} > -
- {Object.keys(zonesData).map((zoneName, index) => ( -
{ - setSelectedZone({ - zoneName, - ...zonesData[zoneName], - }); - }} - > - {zoneName} -
- ))} -
+ {!isPlaying && ( diff --git a/app/src/components/ui/inputs/MultiLevelDropDown.tsx b/app/src/components/ui/inputs/MultiLevelDropDown.tsx index 215bbd8..477111a 100644 --- a/app/src/components/ui/inputs/MultiLevelDropDown.tsx +++ b/app/src/components/ui/inputs/MultiLevelDropDown.tsx @@ -1,5 +1,4 @@ -import React, { useState } from "react"; - +import React, { useState, useRef, useEffect } from "react"; // Dropdown Item Component const DropdownItem = ({ @@ -27,9 +26,11 @@ const DropdownItem = ({ const NestedDropdown = ({ label, children, + onSelect, }: { label: string; children: React.ReactNode; + onSelect: (selectedLabel: string) => void; }) => { const [open, setOpen] = useState(false); @@ -37,36 +38,47 @@ const NestedDropdown = ({
{/* Dropdown Trigger */}
setOpen(!open)} // Toggle submenu on click > {label} {open ? "▼" : "▶"}
{/* Submenu */} - {open &&
{children}
} + {open && ( +
+ {React.Children.map(children, (child) => + React.cloneElement(child as React.ReactElement, { onSelect }) + )} +
+ )}
); }; // Recursive Function to Render Nested Data -const renderNestedData = (data: Record) => { +const renderNestedData = ( + data: Record, + onSelect: (selectedLabel: string) => void +) => { return Object.entries(data).map(([key, value]) => { if (typeof value === "object" && !Array.isArray(value)) { // If the value is an object, render it as a nested dropdown return ( - - {renderNestedData(value)} + + {renderNestedData(value, onSelect)} ); } else if (Array.isArray(value)) { // If the value is an array, render each item as a dropdown item return value.map((item, index) => ( - + onSelect(item)} /> )); } else { // If the value is a simple string, render it as a dropdown item - return ; + return ( + onSelect(value)} /> + ); } }); }; @@ -74,21 +86,52 @@ const renderNestedData = (data: Record) => { // Main Multi-Level Dropdown Component const MultiLevelDropdown = ({ data }: { data: Record }) => { const [open, setOpen] = useState(false); + const [selectedLabel, setSelectedLabel] = useState("Dropdown trigger"); + const dropdownRef = useRef(null); + + // Handle outer click to close the dropdown + useEffect(() => { + const handleClickOutside = (event: MouseEvent) => { + if ( + dropdownRef.current && + !dropdownRef.current.contains(event.target as Node) + ) { + setOpen(false); + } + }; + + document.addEventListener("mousedown", handleClickOutside); + return () => { + document.removeEventListener("mousedown", handleClickOutside); + }; + }, []); + + // Handle selection of an item + const handleSelect = (selectedLabel: string) => { + setSelectedLabel(selectedLabel); // Update the dropdown trigger text + setOpen(false); // Close the dropdown + }; return ( -
+
{/* Dropdown Trigger Button */} {/* Dropdown Menu */} - {open &&
{renderNestedData(data)}
} + {open && ( +
+
+ {renderNestedData(data, handleSelect)} +
+
+ )}
); }; -export default MultiLevelDropdown; \ No newline at end of file +export default MultiLevelDropdown; diff --git a/app/src/modules/visualization/handleSaveTemplate.ts b/app/src/modules/visualization/handleSaveTemplate.ts index 290395a..c489688 100644 --- a/app/src/modules/visualization/handleSaveTemplate.ts +++ b/app/src/modules/visualization/handleSaveTemplate.ts @@ -2,35 +2,73 @@ import { Template } from "../../store/useTemplateStore"; import { captureVisualization } from "./captureVisualization"; type HandleSaveTemplateProps = { - addTemplate: (template: Template) => void; - selectedZone: { - panelOrder: string[]; // Adjust the type based on actual data structure - widgets: any[]; // Replace `any` with the actual widget type - }; + addTemplate: (template: Template) => void; + selectedZone: { + panelOrder: string[]; // Adjust the type based on actual data structure + widgets: any[]; // Replace `any` with the actual widget type + }; + templates?: Template[]; }; // Generate a unique ID (placeholder function) const generateUniqueId = (): string => { - return Math.random().toString(36).substring(2, 15); + return `${Date.now()}-${Math.random().toString(36).substring(2, 15)}`; }; // Refactored function export const handleSaveTemplate = async ({ - addTemplate, - selectedZone, + addTemplate, + selectedZone, + templates = [], }: HandleSaveTemplateProps): Promise => { - try { - const snapshot = await captureVisualization(); - const template: Template = { - id: generateUniqueId(), - name: `Template ${Date.now()}`, - panelOrder: selectedZone.panelOrder, - widgets: selectedZone.widgets, - snapshot, - }; - console.log('template: ', template); - addTemplate(template); - } catch (error) { - console.error('Failed to save template:', error); + try { + // Check if the selected zone has any widgets + if (!selectedZone.widgets || selectedZone.widgets.length === 0) { + console.warn("Cannot save an empty template."); + return; } + + // Check if the template already exists + const isDuplicate = templates.some((template) => { + const isSamePanelOrder = + JSON.stringify(template.panelOrder) === + JSON.stringify(selectedZone.panelOrder); + const isSameWidgets = + JSON.stringify(template.widgets) === + JSON.stringify(selectedZone.widgets); + return isSamePanelOrder && isSameWidgets; + }); + + if (isDuplicate) { + console.warn("This template already exists."); + return; + } + + // Capture visualization snapshot + const snapshot = await captureVisualization(); + if (!snapshot) { + console.error("Failed to capture visualization snapshot."); + return; + } + + // Create a new template + const newTemplate: Template = { + id: generateUniqueId(), + name: `Template ${Date.now()}`, + panelOrder: selectedZone.panelOrder, + widgets: selectedZone.widgets, + snapshot, + }; + + console.log("Saving template:", newTemplate); + + // Save the template + try { + addTemplate(newTemplate); + } catch (error) { + console.error("Failed to add template:", error); + } + } catch (error) { + console.error("Failed to save template:", error); + } }; diff --git a/app/src/styles/abstracts/variables.scss b/app/src/styles/abstracts/variables.scss index eaab86b..b5244a6 100644 --- a/app/src/styles/abstracts/variables.scss +++ b/app/src/styles/abstracts/variables.scss @@ -35,7 +35,7 @@ $highlight-accent-color-dark: #403e6a; // Highlighted accent for dark mode $background-color: #fcfdfd; // Main background color $background-color-dark: #19191d; // Main background color for dark mode $background-color-secondary: #e1e0ff80; // Secondary background color -$background-color-secondary-dark: #1f1f2399; // Secondary background color for dark mode +$background-color-secondary-dark: #39394f99; // Secondary background color for dark mode // Border colors $border-color: #e0dfff; // Default border color diff --git a/app/src/styles/components/_regularDropDown.scss b/app/src/styles/components/_regularDropDown.scss index 085a5cd..86ac078 100644 --- a/app/src/styles/components/_regularDropDown.scss +++ b/app/src/styles/components/_regularDropDown.scss @@ -36,9 +36,11 @@ overflow-y: auto; // Optional: Enable scrolling if content exceeds height left: 0; top: 104%; + .option { padding: 5px; cursor: pointer; + flex-direction: row !important; &:hover { background-color: var(--primary-color); // Optional: Hover effect diff --git a/app/src/styles/components/visualization/floating/energyConsumed.scss b/app/src/styles/components/visualization/floating/energyConsumed.scss new file mode 100644 index 0000000..daec86c --- /dev/null +++ b/app/src/styles/components/visualization/floating/energyConsumed.scss @@ -0,0 +1,110 @@ +@use "../../../abstracts/variables" as *; +@use "../../../abstracts/mixins" as *; + +.floatingWidgets-wrapper { + display: flex; + flex-direction: column; + gap: 6px; + padding-top: 12px; + padding: 6px; + + .floating { + + min-height: 170px; + background: var(--background-color); + border: 1.23px solid var(--border-color); + box-shadow: 0px 4.91px 4.91px 0px #0000001c; + border-radius: $border-radius-medium; + padding: 12px 6px; + } + + .working-state { + display: flex; + flex-direction: column; + gap: 6px; + + .state-working-top { + display: flex; + } + } +} + + + +.floatingWidgets-wrapper { + font-family: Arial, sans-serif; + color: #333; +} + +.floating.working-state { + width: 100%; + height: 283px; + background: #f5f5f5; + border-radius: 8px; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); + padding: 20px; + box-sizing: border-box; + +} + +.state-working-top { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 20px; + // flex-direction: column; +} + +.state { + font-size: 24px; + font-weight: bold; +} + +.working-status { + display: flex; + align-items: center; + gap: 8px; +} + +.working { + font-size: 20px; + color: #4CAF50; +} + +.dot { + display: inline-block; + width: 10px; + height: 10px; + background: #4CAF50; + border-radius: 50%; +} + +.img img { + width: 150px; + height: 100px; + border-radius: 4px; + object-fit: cover; +} + +.state-working-data { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 15px; +} + +.data-row { + display: flex; + justify-content: space-between; + align-items: center; + font-size: 16px; + padding: 4px 0; +} + +.data-key { + color: #666; +} + +.data-value { + font-weight: bold; + color: #333; +} \ No newline at end of file diff --git a/app/src/styles/layout/sidebar.scss b/app/src/styles/layout/sidebar.scss index 0daeca3..54ebf0d 100644 --- a/app/src/styles/layout/sidebar.scss +++ b/app/src/styles/layout/sidebar.scss @@ -78,14 +78,14 @@ display: flex; flex-direction: column; gap: 8px; - padding-right: 6px; + padding: 6px; flex-wrap: nowrap; overflow: auto; .chart { min-height: 170px; - background: var(--background-primary, #fcfdfd); - border: 1.23px solid var(--Grays-Gray-5, #e5e5ea); + background: var(--background-color); + border: 1.23px solid var(--border-color); box-shadow: 0px 4.91px 4.91px 0px #0000001c; border-radius: $border-radius-medium; padding: 12px 6px; @@ -107,7 +107,7 @@ .stock { padding: 13px 5px; - background-color: #e0dfff80; + background-color: var(--background-color-secondary); border-radius: 6.33px; display: flex; justify-content: space-between; @@ -291,8 +291,46 @@ gap: 12px; padding: 10px 12px; + .datas { + display: flex; + align-items: center; + justify-content: space-between; + + .datas__class { + display: flex; + align-items: center; + + .multi-level-dropdown { + + min-width: 100px; + + .dropdown-button { + display: flex; + justify-content: space-between; + gap: 6px; + } + } + } + + .datas__class { + display: flex; + gap: 12px; + + .datas__separator {} + + .disable { + cursor: not-allowed; + pointer-events: none; + /* Disables all mouse interactions */ + opacity: 0.5; + /* Optional: Makes the button look visually disabled */ + } + + } + } + .sideBarHeader { - color: #5c87df; + color: var(--accent-color); border-bottom: 1px solid var(--border-color); padding-bottom: 6px; } @@ -388,7 +426,7 @@ width: 100%; height: 150px; background: #f0f0f0; - border-radius: 8px; + // border-radius: 8px; } .optionsContainer { @@ -523,93 +561,119 @@ } } -/* Base styles */ + + + + + + + + + + + + .multi-level-dropdown { position: relative; display: inline-block; - text-align: left; .dropdown-button { - background-color: #3b82f6; /* Blue background */ - color: white; - padding: 0.5rem 1rem; - font-size: 0.875rem; - border: none; - border-radius: 0.375rem; + width: 100%; + background-color: var(--background-color) !important; + border: 1px solid var(--border-color) !important; + padding: 5px 10px; + + + // font-size: 12px; cursor: pointer; - display: flex; - align-items: center; - transition: background-color 0.2s ease; + border-radius: 5px; + transition: background-color 0.3s ease; &:hover { - background-color: #2563eb; /* Darker blue on hover */ + background-color: #333333; } - .icon { - margin-left: 0.5rem; + &.open { + background-color: #333333; } } .dropdown-menu { position: absolute; - top: calc(100% + 0.5rem); /* Add spacing below the button */ + top: 100%; left: 0; - width: 12rem; - background-color: white; - border: 1px solid #e5e7eb; /* Light gray border */ - border-radius: 0.375rem; - box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); /* Subtle shadow */ - z-index: 10; - } -} + background-color: #ffffff; + border: 1px solid #cccccc; + border-radius: 5px; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); + z-index: 1000; + min-width: 200px; + overflow: auto; + max-height: 600px; -/* Dropdown Item */ -.dropdown-item { - display: block; - padding: 0.5rem 1rem; - font-size: 0.875rem; - color: #4b5563; /* Gray text */ - text-decoration: none; - cursor: pointer; - transition: background-color 0.2s ease; + .dropdown-content { + display: flex; + flex-direction: column; + gap: 6px; - &:hover { - background-color: #f3f4f6; /* Light gray background on hover */ - } -} + .nested-dropdown { + // &:first-child{ + margin-left: 0; + // } + } -/* Nested Dropdown */ -.nested-dropdown { - position: relative; - - .dropdown-trigger { - display: flex; - justify-content: space-between; - align-items: center; - padding: 0.5rem 1rem; - font-size: 0.875rem; - color: #4b5563; /* Gray text */ - cursor: pointer; - transition: background-color 0.2s ease; - - &:hover { - background-color: #f3f4f6; /* Light gray background on hover */ + padding: 10px; } - .icon { - margin-left: 0.5rem; + .dropdown-item { + display: block; + padding: 5px 10px; + text-decoration: none; + color: #000000; + font-size: 14px; + cursor: pointer; + transition: background-color 0.3s ease; + + &:hover { + background-color: #f0f0f0; + } + } + + .nested-dropdown { + margin-left: 20px; + + .dropdown-trigger { + display: flex; + align-items: center; + justify-content: space-between; + padding: 5px 10px; + cursor: pointer; + font-size: 14px; + color: #000000; + transition: background-color 0.3s ease; + + &:hover { + background-color: #f0f0f0; + } + + &.open { + background-color: #e0e0e0; + } + + .icon { + font-size: 12px; + margin-left: 5px; + } + } + + .submenu { + margin-top: 5px; + padding-left: 20px; + border-left: 2px solid #cccccc; + display: flex; + flex-direction: column; + gap: 6px; + } } } - - .submenu { - position: absolute; - top: 0; - left: 100%; /* Position submenu to the right */ - width: 12rem; - background-color: white; - border: 1px solid #e5e7eb; /* Light gray border */ - border-radius: 0.375rem; - box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); /* Subtle shadow */ - z-index: 20; - } -} +} \ No newline at end of file diff --git a/app/src/styles/main.scss b/app/src/styles/main.scss index 68688f6..90f6419 100644 --- a/app/src/styles/main.scss +++ b/app/src/styles/main.scss @@ -19,6 +19,7 @@ @use 'components/templates'; @use 'components/tools'; @use 'components/regularDropDown'; +@use 'components/visualization/floating/energyConsumed'; // layout @use 'layout/sidebar'; diff --git a/app/src/styles/pages/realTimeViz.scss b/app/src/styles/pages/realTimeViz.scss index 828cc7b..a7c1181 100644 --- a/app/src/styles/pages/realTimeViz.scss +++ b/app/src/styles/pages/realTimeViz.scss @@ -4,7 +4,7 @@ .realTime-viz { background-color: var(--background-color); border-radius: 20px; - box-shadow: 0px 4px 8px rgba(60, 60, 67, 0.1019607843); + box-shadow: $box-shadow-medium; width: calc(100% - (320px + 270px + 80px)); height: 600px; position: absolute; @@ -30,7 +30,7 @@ .zoon-wrapper { display: flex; - background-color: #e0dfff80; + background-color: var(--background-color); position: absolute; bottom: 10px; left: 50%; @@ -40,14 +40,14 @@ border-radius: 8px; max-width: 80%; overflow: auto; - + max-width: calc(100% - 450px); &::-webkit-scrollbar { display: none; } .zone { width: auto; - background-color: #fcfdfd; + background-color: var(--background-color); border-radius: 6px; padding: 4px 8px; white-space: nowrap; @@ -83,43 +83,6 @@ } } - @media (max-width: 768px) { - width: 90%; // Take even more width on very small screens - height: 400px; // Further reduce height - top: 45%; // Adjust vertical position slightly upward - - .panel { - &.top-panel, - &.bottom-panel { - .panel-content { - flex-direction: column; // Stack panels vertically on small screens - - .chart-container { - width: 100%; // Make charts full width - height: 150px; // Reduce chart height - } - } - } - } - } - - @media (max-width: 480px) { - width: 95%; // Take almost full width on very small devices - height: 350px; // Further reduce height - top: 40%; // Move slightly higher for better visibility - - .side-button-container { - flex-direction: row !important; // Force buttons into a row - gap: 4px; // Reduce spacing between buttons - - &.top, - &.bottom { - left: 50%; // Center horizontally - transform: translateX(-50%); - } - } - } - .content-container { display: flex; height: 100vh; @@ -189,6 +152,7 @@ display: flex; flex-direction: column; gap: 10px; + background-color: var(--background-color); &::-webkit-scrollbar { display: none; @@ -216,18 +180,23 @@ } } + &.top-panel, &.bottom-panel { left: 0; right: 0; + .fullScreen { + background-color: red; + } + .panel-content { display: flex; flex-direction: row; .chart-container { height: 100%; - width: 230px; + width: 200px; } } } @@ -247,7 +216,7 @@ .chart-container { width: 100%; - height: 200px; + height: 180px; } } @@ -275,6 +244,15 @@ .icon { display: flex; + align-items: center; + justify-content: center; + width: 18px; + height: 18px; + border-radius: 4px; + } + + .active { + background-color: var(--accent-color); } &:hover { @@ -409,4 +387,4 @@ } } } -} +} \ No newline at end of file