diff --git a/app/.gitignore b/app/.gitignore index 2f5cb0a..d1f5e53 100644 --- a/app/.gitignore +++ b/app/.gitignore @@ -1,29 +1,29 @@ -# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. - -# dependencies -/node_modules -# /package-lock.json -/.pnp -.pnp.js - -# testing -/coverage - -# production -/build - -# misc -.DS_Store -.env.local -.env.development.local -.env.test.local -.env.production.local - -npm-debug.log* -yarn-debug.log* -yarn-error.log* - - -# remove zip -*.zip -**/temp/ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +# /package-lock.json +/.pnp +.pnp.js + +# testing +/coverage + +# production +/build + +# misc +.DS_Store +.env.local +.env.development.local +.env.test.local +.env.production.local + +npm-debug.log* +yarn-debug.log* +yarn-error.log* + + +# remove zip +*.zip +**/temp/ diff --git a/app/src/components/icons/3dChartIcons.tsx b/app/src/components/icons/3dChartIcons.tsx index 320b018..d6dcb26 100644 --- a/app/src/components/icons/3dChartIcons.tsx +++ b/app/src/components/icons/3dChartIcons.tsx @@ -8,8 +8,8 @@ export function ThroughputIcon() { xmlns="http://www.w3.org/2000/svg" > diff --git a/app/src/components/icons/ExportCommonIcons.tsx b/app/src/components/icons/ExportCommonIcons.tsx index dd170f8..d2c9970 100644 --- a/app/src/components/icons/ExportCommonIcons.tsx +++ b/app/src/components/icons/ExportCommonIcons.tsx @@ -511,3 +511,99 @@ export function AI_Icon() { ); } + +export const KebabIcon = () => { + return ( + + + + + + ); +}; + +export const DublicateIcon = () => { + return ( + + + + ); +}; + +export const DeleteIcon = () => { + return ( + + + + + + + + ); +}; diff --git a/app/src/components/icons/SimulationIcons.tsx b/app/src/components/icons/SimulationIcons.tsx index 5bc3296..28ce559 100644 --- a/app/src/components/icons/SimulationIcons.tsx +++ b/app/src/components/icons/SimulationIcons.tsx @@ -93,3 +93,114 @@ export function SimulationIcon({ isActive }: { isActive: boolean }) { ); } + +// simulation player icons + +export function ResetIcon() { + return ( + + + + + + ); +} + +export function PlayStopIcon() { + return ( + + + + ); +} + +export function ExitIcon() { + return ( + + + + ); +} + +export function MoveArrowRight() { + return ( + + + + ); +} + +export function MoveArrowLeft() { + return ( + + + + ); +} diff --git a/app/src/components/icons/marketPlaceIcons.tsx b/app/src/components/icons/marketPlaceIcons.tsx index 76dedd3..f0c1cc1 100644 --- a/app/src/components/icons/marketPlaceIcons.tsx +++ b/app/src/components/icons/marketPlaceIcons.tsx @@ -10,8 +10,8 @@ export function StarsIcon() { ); @@ -26,14 +26,14 @@ export function DownloadIcon() { xmlns="http://www.w3.org/2000/svg" > @@ -53,14 +53,14 @@ export function EyeIconBig() { ); @@ -77,8 +77,8 @@ export function CommentsIcon() { > @@ -102,8 +102,8 @@ export function VerifiedIcon() { xmlns="http://www.w3.org/2000/svg" > @@ -123,9 +123,9 @@ export function StarsIconSmall() { ); @@ -144,9 +144,9 @@ export function FiileedStarsIconSmall() { d="M5.07372 1.61354C5.20877 1.31056 5.27632 1.15908 5.37035 1.11243C5.45202 1.0719 5.54791 1.0719 5.62958 1.11243C5.72362 1.15908 5.79117 1.31056 5.92621 1.61354L7.00187 4.02675C7.04183 4.11631 7.06178 4.16109 7.0927 4.19539C7.12 4.22573 7.15342 4.25 7.1907 4.26662C7.23287 4.2854 7.28164 4.29055 7.37917 4.30084L10.0067 4.57816C10.3366 4.61297 10.5015 4.63038 10.5749 4.70539C10.6387 4.77054 10.6683 4.86177 10.655 4.95198C10.6397 5.05581 10.5165 5.16682 10.2701 5.3889L8.30737 7.15768C8.23457 7.22331 8.19811 7.25615 8.17507 7.29611C8.15466 7.33152 8.14188 7.37077 8.13762 7.41137C8.13278 7.45728 8.14293 7.50523 8.16329 7.60119L8.71151 10.1858C8.78034 10.5103 8.81476 10.6725 8.76611 10.7655C8.72381 10.8463 8.64623 10.9027 8.55634 10.9179C8.45286 10.9354 8.30918 10.8526 8.02183 10.6868L5.73312 9.36677C5.64819 9.31777 5.60572 9.29332 5.56057 9.2837C5.52062 9.27524 5.47931 9.27524 5.43936 9.2837C5.39421 9.29332 5.35174 9.31777 5.26681 9.36677L2.97811 10.6868C2.69077 10.8526 2.5471 10.9354 2.44361 10.9179C2.35372 10.9027 2.27611 10.8463 2.23385 10.7655C2.18521 10.6725 2.21962 10.5103 2.28845 10.1858L2.83664 7.60119C2.85699 7.50523 2.86716 7.45728 2.86233 7.41137C2.85805 7.37077 2.8453 7.33152 2.82488 7.29611C2.80181 7.25615 2.76539 7.22331 2.69254 7.15768L0.72985 5.3889C0.483444 5.16682 0.360238 5.05581 0.34492 4.95198C0.33162 4.86177 0.361259 4.77054 0.425041 4.70539C0.498471 4.63038 0.663408 4.61297 0.993283 4.57816L3.62079 4.30084C3.71831 4.29055 3.76706 4.2854 3.80923 4.26662C3.84653 4.25 3.87993 4.22573 3.90727 4.19539C3.93815 4.16109 3.95812 4.11631 3.99804 4.02675L5.07372 1.61354Z" fill="#F3A50C" stroke="#F3A50C" - stroke-width="0.7" - stroke-linecap="round" - stroke-linejoin="round" + strokeWidth="0.7" + strokeLinecap="round" + strokeLinejoin="round" /> ); diff --git a/app/src/components/layout/sidebarRight/visualization/Visualization.tsx b/app/src/components/layout/sidebarRight/visualization/Visualization.tsx index ec4d3ad..c2ac477 100644 --- a/app/src/components/layout/sidebarRight/visualization/Visualization.tsx +++ b/app/src/components/layout/sidebarRight/visualization/Visualization.tsx @@ -13,7 +13,7 @@ const Visualization = () => { return (
diff --git a/app/src/components/ui/Tools.tsx b/app/src/components/ui/Tools.tsx index 9d06df9..abe7ba8 100644 --- a/app/src/components/ui/Tools.tsx +++ b/app/src/components/ui/Tools.tsx @@ -285,9 +285,13 @@ const Tools: React.FC = () => {
) : ( -
setIsPlaying(false)}> - X -
+ <> + {activeModule !== "simulation" && ( +
setIsPlaying(false)}> + X +
+ )} + )} ); diff --git a/app/src/components/ui/charts/PieGraphComponent.tsx b/app/src/components/ui/charts/PieGraphComponent.tsx index 0066ec3..7c8bb1d 100644 --- a/app/src/components/ui/charts/PieGraphComponent.tsx +++ b/app/src/components/ui/charts/PieGraphComponent.tsx @@ -53,7 +53,6 @@ const PieChartComponent = ({ .getPropertyValue("--accent-color") .trim(); - console.log("accentColor: ", accentColor); const options = useMemo( () => ({ responsive: true, diff --git a/app/src/components/ui/componets/AddButtons.tsx b/app/src/components/ui/componets/AddButtons.tsx index 42dc082..7185fc6 100644 --- a/app/src/components/ui/componets/AddButtons.tsx +++ b/app/src/components/ui/componets/AddButtons.tsx @@ -5,6 +5,7 @@ import { LockIcon, } from "../../icons/RealTimeVisulationIcons"; import { panelData } from "../../../services/realTimeVisulization/zoneData/panel"; +import { AddIcon } from "../../icons/ExportCommonIcons"; // Define the type for `Side` type Side = "top" | "bottom" | "left" | "right"; @@ -18,7 +19,7 @@ interface ButtonsProps { lockedPanels: Side[]; zoneId: string; zoneViewPortTarget: number[]; - zoneViewPortPosition: number[] + zoneViewPortPosition: number[]; widgets: { id: string; type: string; @@ -35,7 +36,7 @@ interface ButtonsProps { lockedPanels: Side[]; zoneId: string; zoneViewPortTarget: number[]; - zoneViewPortPosition: number[] + zoneViewPortPosition: number[]; widgets: { id: string; type: string; @@ -116,7 +117,7 @@ const AddButtons: React.FC = ({ }; // Delete the selectedZone state - console.log('updatedZone: ', updatedZone); + console.log("updatedZone: ", updatedZone); setSelectedZone(updatedZone); } else { // If the panel is not active, activate it @@ -127,8 +128,8 @@ const AddButtons: React.FC = ({ activeSides: newActiveSides, panelOrder: newActiveSides, }; - const email = localStorage.getItem('email') - const organization = (email!.split("@")[1]).split(".")[0]; + const email = localStorage.getItem("email"); + const organization = email!.split("@")[1].split(".")[0]; // let response = panelData(organization, selectedZone.zoneId, newActiveSides) // console.log('response: ', response); @@ -145,7 +146,9 @@ const AddButtons: React.FC = ({
{/* "+" Button */} {/* Extra Buttons */} @@ -161,19 +166,16 @@ const AddButtons: React.FC = ({
{/* Hide Panel */}
toggleVisibility(side)} >
@@ -188,8 +190,9 @@ const AddButtons: React.FC = ({ {/* Lock/Unlock Panel */}
= ({ } onClick={() => toggleLockPanel(side)} > - +
)} diff --git a/app/src/components/ui/componets/DisplayZone.tsx b/app/src/components/ui/componets/DisplayZone.tsx index 56c67bb..9f1028f 100644 --- a/app/src/components/ui/componets/DisplayZone.tsx +++ b/app/src/components/ui/componets/DisplayZone.tsx @@ -1,5 +1,6 @@ -import React, { useEffect, useRef } from "react"; +import React, { useEffect, useRef, useState, useCallback } from "react"; import { Widget } from "../../../store/useWidgetStore"; +import { MoveArrowLeft, MoveArrowRight } from "../../icons/SimulationIcons"; // Define the type for `Side` type Side = "top" | "bottom" | "left" | "right"; @@ -60,138 +61,128 @@ const DisplayZone: React.FC = ({ // 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 - ); - // console.log('setSelectedOption: ', setSelectedOption); - const [options, setOptions] = React.useState([]); - // console.log('setOptions: ', setOptions); + // State to track overflow visibility + const [showLeftArrow, setShowLeftArrow] = useState(false); + const [showRightArrow, setShowRightArrow] = useState(false); - // Scroll to the selected option when it changes - useEffect(() => { + // Function to calculate overflow state + const updateOverflowState = useCallback(() => { 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); - } + const isOverflowing = container.scrollWidth > container.clientWidth; + const canScrollLeft = container.scrollLeft > 0; + const canScrollRight = + container.scrollLeft + container.clientWidth < container.scrollWidth; - return () => { - if (container) { - container.removeEventListener("wheel", handleWheel); - container.removeEventListener("mousedown", handleMouseDown); - container.removeEventListener("mousemove", handleMouseMove); - container.removeEventListener("mouseup", handleMouseUp); - container.removeEventListener("mouseleave", handleMouseLeave); - } - }; + setShowLeftArrow(isOverflowing && canScrollLeft); + setShowRightArrow(isOverflowing && canScrollRight); + } }, []); - // 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()); + useEffect(() => { + const container = containerRef.current; + + if (container) { + // Initial calculation after the DOM has been rendered + const handleInitialRender = () => { + requestAnimationFrame(updateOverflowState); + }; + + handleInitialRender(); + + // Update on window resize or scroll + const handleResize = () => updateOverflowState(); + const handleScroll = () => updateOverflowState(); + + // Add mouse wheel listener for horizontal scrolling + const handleWheel = (event: WheelEvent) => { + event.preventDefault(); // Prevent default vertical scrolling + if (container) { + container.scrollBy({ + left: event.deltaY * 2, // Translate vertical scroll to horizontal scroll + behavior: "smooth", + }); + } + }; + + container.addEventListener("scroll", handleScroll); + window.addEventListener("resize", handleResize); + container.addEventListener("wheel", handleWheel, { passive: false }); + + return () => { + container.removeEventListener("scroll", handleScroll); + window.removeEventListener("resize", handleResize); + container.removeEventListener("wheel", handleWheel); + }; + } + }, [updateOverflowState]); + + // Handle scrolling with navigation arrows + const handleScrollLeft = () => { + const container = containerRef.current; + if (container) { + container.scrollBy({ + left: -200, // Scroll left by 200px + behavior: "smooth", + }); + } + }; + + const handleScrollRight = () => { + const container = containerRef.current; + if (container) { + container.scrollBy({ + left: 200, // Scroll right by 200px + behavior: "smooth", + }); + } }; return ( -
- {Object.keys(zonesData).map((zoneName, index) => ( -
+ {/* Left Arrow */} + {showLeftArrow && ( + + )} + + {/* Zones Wrapper */} +
+ {Object.keys(zonesData).map((zoneName, index) => ( +
{ + onClick={() => { + console.log("zoneName: ", zoneName); + setSelectedZone({ + zoneName, + activeSides: zonesData[zoneName].activeSides || [], + panelOrder: zonesData[zoneName].panelOrder || [], + lockedPanels: zonesData[zoneName].lockedPanels || [], + widgets: zonesData[zoneName].widgets || [], + zoneId: zonesData[zoneName]?.zoneId || "", + zoneViewPortTarget: + zonesData[zoneName].zoneViewPortTarget || [], + zoneViewPortPosition: + zonesData[zoneName].zoneViewPortPosition || [], + }); + }} + > + {zoneName} +
+ ))} +
- - setSelectedZone({ - zoneName, - activeSides: zonesData[zoneName].activeSides || [], - panelOrder: zonesData[zoneName].panelOrder || [], - lockedPanels: zonesData[zoneName].lockedPanels || [], - widgets: zonesData[zoneName].widgets || [], - zoneId: zonesData[zoneName]?.zoneId || "", - zoneViewPortTarget: zonesData[zoneName].zoneViewPortTarget || [], - zoneViewPortPosition: - zonesData[zoneName].zoneViewPortPosition || [], - }); - // setSelectedZone({ - // zoneName, - // ...zonesData[zoneName], - // }); - }} - > - {zoneName} -
- ))} + {/* Right Arrow */} + {showRightArrow && ( + + )}
); }; diff --git a/app/src/components/ui/componets/DraggableWidget.tsx b/app/src/components/ui/componets/DraggableWidget.tsx index 9172fa4..339e7a6 100644 --- a/app/src/components/ui/componets/DraggableWidget.tsx +++ b/app/src/components/ui/componets/DraggableWidget.tsx @@ -1,164 +1,317 @@ -import { useWidgetStore } from "../../../store/useWidgetStore"; -import ProgressCard from "../realTimeVis/charts/ProgressCard"; -import PieGraphComponent from "../realTimeVis/charts/PieGraphComponent"; -import BarGraphComponent from "../realTimeVis/charts/BarGraphComponent"; -import LineGraphComponent from "../realTimeVis/charts/LineGraphComponent"; -import RadarGraphComponent from "../realTimeVis/charts/RadarGraphComponent"; -import DoughnutGraphComponent from "../realTimeVis/charts/DoughnutGraphComponent"; -import PolarAreaGraphComponent from "../realTimeVis/charts/PolarAreaGraphComponent"; - -export const DraggableWidget = ({ - widget, - hiddenPanels, // Add this prop to track hidden panels - index, onReorder -}: { - widget: any; - hiddenPanels: string[]; // Array of hidden panel names - index: number; onReorder: (fromIndex: number, toIndex: number) => void -}) => { - const { selectedChartId, setSelectedChartId } = useWidgetStore(); - - const handlePointerDown = () => { - if (selectedChartId?.id !== widget.id) { - setSelectedChartId(widget); - } - }; - - // Determine if the widget's panel is hidden - const isPanelHidden = hiddenPanels.includes(widget.panel); - - const handleDragStart = (event: React.DragEvent) => { - event.dataTransfer.setData('text/plain', index.toString()); // Store the index of the dragged widget - }; - const handleDragEnter = (event: React.DragEvent) => { - event.preventDefault(); // Allow drop - }; - - const handleDragOver = (event: React.DragEvent) => { - event.preventDefault(); // Allow drop - }; - - const handleDrop = (event: React.DragEvent) => { - event.preventDefault(); - const fromIndex = parseInt(event.dataTransfer.getData('text/plain'), 10); // Get the dragged widget's index - const toIndex = index; // The index of the widget where the drop occurred - if (fromIndex !== toIndex) { - onReorder(fromIndex, toIndex); // Call the reorder function passed as a prop - } - }; - - - return ( - <> - - - ); -}; +import { useWidgetStore } from "../../../store/useWidgetStore"; +import ProgressCard from "../realTimeVis/charts/ProgressCard"; +import PieGraphComponent from "../realTimeVis/charts/PieGraphComponent"; +import BarGraphComponent from "../realTimeVis/charts/BarGraphComponent"; +import LineGraphComponent from "../realTimeVis/charts/LineGraphComponent"; +import DoughnutGraphComponent from "../realTimeVis/charts/DoughnutGraphComponent"; +import PolarAreaGraphComponent from "../realTimeVis/charts/PolarAreaGraphComponent"; +import { + DeleteIcon, + DublicateIcon, + KebabIcon, +} from "../../icons/ExportCommonIcons"; +import { useEffect, useRef, useState } from "react"; + +type Side = "top" | "bottom" | "left" | "right"; + +interface Widget { + id: string; + type: string; + title: string; + panel: Side; + data: any; +} + +export const DraggableWidget = ({ + widget, + hiddenPanels, + index, + onReorder, + openKebabId, + setOpenKebabId, + selectedZone, + setSelectedZone, +}: { + selectedZone: { + zoneName: string; + activeSides: Side[]; + panelOrder: Side[]; + lockedPanels: Side[]; + widgets: Widget[]; + }; + setSelectedZone: React.Dispatch< + React.SetStateAction<{ + zoneName: string; + activeSides: Side[]; + panelOrder: Side[]; + lockedPanels: Side[]; + zoneId: string; + zoneViewPortTarget: number[]; + zoneViewPortPosition: number[]; + widgets: { + id: string; + type: string; + title: string; + panel: Side; + data: any; + }[]; + }> + >; + + widget: any; + hiddenPanels: string[]; + index: number; + onReorder: (fromIndex: number, toIndex: number) => void; + openKebabId: string | null; + setOpenKebabId: (id: string | null) => void; +}) => { + const { selectedChartId, setSelectedChartId } = useWidgetStore(); + const [panelDimensions, setPanelDimensions] = useState<{ + [side in Side]?: { width: number; height: number }; + }>({}); + const handlePointerDown = () => { + if (selectedChartId?.id !== widget.id) { + setSelectedChartId(widget); + } + }; + + const isPanelHidden = hiddenPanels.includes(widget.panel); + + const deleteSelectedChart = () => { + const updatedWidgets = selectedZone.widgets.filter( + (w: Widget) => w.id !== widget.id + ); + + setSelectedZone((prevZone: any) => ({ + ...prevZone, + widgets: updatedWidgets, + })); + + setOpenKebabId(null); + }; + + const getCurrentWidgetCount = (panel: Side) => + selectedZone.widgets.filter((w) => w.panel === panel).length; + + const calculatePanelCapacity = (panel: Side) => { + const CHART_WIDTH = 150; + const CHART_HEIGHT = 150; + 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 isPanelFull = (panel: Side) => { + const currentWidgetCount = getCurrentWidgetCount(panel); + const panelCapacity = calculatePanelCapacity(panel); + return currentWidgetCount >= panelCapacity; + }; + + const duplicateWidget = () => { + const duplicatedWidget: Widget = { + ...widget, + id: `${widget.id}-copy-${Date.now()}`, + }; + + setSelectedZone((prevZone: any) => ({ + ...prevZone, + widgets: [...prevZone.widgets, duplicatedWidget], + })); + + setOpenKebabId(null); + + console.log("Duplicated widget with ID:", duplicatedWidget.id); + }; + + const handleKebabClick = (event: React.MouseEvent) => { + event.stopPropagation(); + if (openKebabId === widget.id) { + setOpenKebabId(null); + } else { + setOpenKebabId(widget.id); + } + }; + + const widgetRef = useRef(null); + + useEffect(() => { + const handleClickOutside = (event: MouseEvent) => { + if ( + widgetRef.current && + !widgetRef.current.contains(event.target as Node) + ) { + setOpenKebabId(null); + } + }; + + document.addEventListener("mousedown", handleClickOutside); + + return () => { + document.removeEventListener("mousedown", handleClickOutside); + }; + }, [setOpenKebabId]); + + const handleDragStart = (event: React.DragEvent) => { + event.dataTransfer.setData("text/plain", index.toString()); // Store the index of the dragged widget + }; + const handleDragEnter = (event: React.DragEvent) => { + event.preventDefault(); // Allow drop + }; + + const handleDragOver = (event: React.DragEvent) => { + event.preventDefault(); // Allow drop + }; + + const handleDrop = (event: React.DragEvent) => { + event.preventDefault(); + const fromIndex = parseInt(event.dataTransfer.getData("text/plain"), 10); // Get the dragged widget's index + const toIndex = index; // The index of the widget where the drop occurred + if (fromIndex !== toIndex) { + onReorder(fromIndex, toIndex); // Call the reorder function passed as a prop + } + }; + + return ( + <> +
+ {/* Kebab Icon */} +
+ +
+ + {/* Kebab Options */} + {openKebabId === widget.id && ( +
+
+
+ +
+
Duplicate
+
+
+
+ +
+
Delete
+
+
+ )} + + {/* Render charts based on widget type */} + {widget.type === "progress" && ( + + )} + {widget.type === "line" && ( + + )} + {widget.type === "bar" && ( + + )} + {widget.type === "pie" && ( + + )} + {widget.type === "doughnut" && ( + + )} + {widget.type === "polarArea" && ( + + )} +
+ + ); +}; diff --git a/app/src/components/ui/componets/Panel.tsx b/app/src/components/ui/componets/Panel.tsx index 6cce425..3fee476 100644 --- a/app/src/components/ui/componets/Panel.tsx +++ b/app/src/components/ui/componets/Panel.tsx @@ -38,6 +38,7 @@ interface PanelProps { }> >; hiddenPanels: string[]; + setZonesData: React.Dispatch>; // Add this line } const generateUniqueId = () => @@ -47,11 +48,13 @@ const Panel: React.FC = ({ selectedZone, setSelectedZone, hiddenPanels, + setZonesData, }) => { 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(); @@ -69,8 +72,9 @@ const Panel: React.FC = ({ case "top": case "bottom": return { - width: `calc(100% - ${(leftActive ? panelSize : 0) + (rightActive ? panelSize : 0) - }px)`, + width: `calc(100% - ${ + (leftActive ? panelSize : 0) + (rightActive ? panelSize : 0) + }px)`, height: `${panelSize - 2}px`, left: leftActive ? `${panelSize}px` : "0", right: rightActive ? `${panelSize}px` : "0", @@ -80,8 +84,9 @@ const Panel: React.FC = ({ case "right": return { width: `${panelSize - 2}px`, - height: `calc(100% - ${(topActive ? panelSize : 0) + (bottomActive ? panelSize : 0) - }px)`, + height: `calc(100% - ${ + (topActive ? panelSize : 0) + (bottomActive ? panelSize : 0) + }px)`, top: topActive ? `${panelSize}px` : "0", bottom: bottomActive ? `${panelSize}px` : "0", [side]: "0", @@ -104,8 +109,8 @@ const Panel: React.FC = ({ if (currentWidgetsCount >= maxCapacity) return; - console.log('draggedAsset: ', draggedAsset); - console.log('panel: ', panel); + console.log("draggedAsset: ", draggedAsset); + console.log("panel: ", panel); addWidgetToPanel(draggedAsset, panel); }; @@ -133,6 +138,7 @@ const Panel: React.FC = ({ : Math.floor(dimensions.height / CHART_HEIGHT); }; + // while dublicate check this and add const addWidgetToPanel = (asset: any, panel: Side) => { const newWidget = { ...asset, @@ -174,7 +180,7 @@ const Panel: React.FC = ({ const handleReorder = (fromIndex: number, toIndex: number, panel: Side) => { if (!selectedZone) return; // Ensure selectedZone is not null - console.log('selectedZone: ', selectedZone); + console.log("selectedZone: ", selectedZone); setSelectedZone((prev) => { if (!prev) return prev; // Ensure prev is not null @@ -197,9 +203,6 @@ const Panel: React.FC = ({ }); }; - - - return ( <> {selectedZone.activeSides.map((side) => ( @@ -237,6 +240,10 @@ const Panel: React.FC = ({ onReorder={(fromIndex, toIndex) => handleReorder(fromIndex, toIndex, side) } + openKebabId={openKebabId} + setOpenKebabId={setOpenKebabId} + selectedZone={selectedZone} + setSelectedZone={setSelectedZone} /> ))}
@@ -247,5 +254,3 @@ const Panel: React.FC = ({ }; export default Panel; - - diff --git a/app/src/components/ui/componets/RealTimeVisulization.tsx b/app/src/components/ui/componets/RealTimeVisulization.tsx index cecc2c0..7f7dc2f 100644 --- a/app/src/components/ui/componets/RealTimeVisulization.tsx +++ b/app/src/components/ui/componets/RealTimeVisulization.tsx @@ -10,7 +10,6 @@ import { useDroppedObjectsStore, useZones } from "../../../store/store"; import DroppedObjects from "./DroppedFloatingWidgets"; - type Side = "top" | "bottom" | "left" | "right"; type FormattedZoneData = Record< @@ -128,7 +127,8 @@ const RealTimeVisulization: React.FC = () => { style={{ height: "100%", width: "100%", - borderRadius: isPlaying || activeModule !== "visualization" ? "" : "6px", + borderRadius: + isPlaying || activeModule !== "visualization" ? "" : "6px", }} onDrop={(event) => handleDrop(event)} onDragOver={(event) => event.preventDefault()} @@ -154,10 +154,10 @@ const RealTimeVisulization: React.FC = () => { )} )} diff --git a/app/src/components/ui/inputs/MultiLevelDropDown.tsx b/app/src/components/ui/inputs/MultiLevelDropDown.tsx index ef74bcf..3385a5b 100644 --- a/app/src/components/ui/inputs/MultiLevelDropDown.tsx +++ b/app/src/components/ui/inputs/MultiLevelDropDown.tsx @@ -141,6 +141,7 @@ // export default MultiLevelDropdown; import React, { useState, useRef, useEffect } from "react"; +import { ArrowIcon } from "../../icons/ExportCommonIcons"; // Dropdown Item Component const DropdownItem = ({ @@ -173,7 +174,13 @@ const NestedDropdown = ({ className={`dropdown-trigger ${open ? "open" : ""}`} onClick={() => setOpen(!open)} > - {label} {open ? "▼" : "▶"} + {label} +
+ +
{open && (
@@ -199,11 +206,11 @@ interface MultiLevelDropdownProps { } // Main Multi-Level Dropdown Component -const MultiLevelDropdown = ({ - data, - onSelect, +const MultiLevelDropdown = ({ + data, + onSelect, onUnselect, - selectedValue + selectedValue, }: MultiLevelDropdownProps) => { const [open, setOpen] = useState(false); const dropdownRef = useRef(null); @@ -236,7 +243,7 @@ const MultiLevelDropdown = ({ }; // Determine the display label - const displayLabel = selectedValue + const displayLabel = selectedValue ? `${selectedValue.name} - ${selectedValue.fields}` : "Dropdown trigger"; @@ -270,4 +277,3 @@ const MultiLevelDropdown = ({ }; export default MultiLevelDropdown; - diff --git a/app/src/components/ui/simulation/simulationPlayer.tsx b/app/src/components/ui/simulation/simulationPlayer.tsx new file mode 100644 index 0000000..85f2c54 --- /dev/null +++ b/app/src/components/ui/simulation/simulationPlayer.tsx @@ -0,0 +1,133 @@ +import React, { useState, useRef, useEffect } from "react"; +import { ExitIcon, PlayStopIcon, ResetIcon } from "../../icons/SimulationIcons"; +import { usePlayButtonStore } from "../../../store/usePlayButtonStore"; + +const SimulationPlayer: React.FC = () => { + const [speed, setSpeed] = useState(1); + const [playSimulation, setPlaySimulation] = useState(false); + const { setIsPlaying } = usePlayButtonStore(); + const sliderRef = useRef(null); + const isDragging = useRef(false); + + // Button functions + const handleReset = () => { + setSpeed(1); + }; + const handlePlayStop = () => { + setPlaySimulation(!playSimulation); + }; + const handleExit = () => { + setPlaySimulation(false); + setIsPlaying(false); + }; + + // Slider functions starts + const handleSpeedChange = (event: React.ChangeEvent) => { + setSpeed(parseFloat(event.target.value)); + }; + + const calculateHandlePosition = () => { + return ((speed - 0.5) / (50 - 0.5)) * 100; + }; + + const handleMouseDown = () => { + isDragging.current = true; + document.addEventListener("mousemove", handleMouseMove); + document.addEventListener("mouseup", handleMouseUp); + }; + + const handleMouseMove = (e: MouseEvent) => { + if (!isDragging.current || !sliderRef.current) return; + + const sliderRect = sliderRef.current.getBoundingClientRect(); + const offsetX = e.clientX - sliderRect.left; + const percentage = Math.min(Math.max(offsetX / sliderRect.width, 0), 1); + const newValue = 0.5 + percentage * (50 - 0.5); + setSpeed(parseFloat(newValue.toFixed(1))); + }; + + const handleMouseUp = () => { + isDragging.current = false; + document.removeEventListener("mousemove", handleMouseMove); + document.removeEventListener("mouseup", handleMouseUp); + }; + + useEffect(() => { + return () => { + document.removeEventListener("mousemove", handleMouseMove); + document.removeEventListener("mouseup", handleMouseUp); + }; + }, []); + // Slider function ends + + return ( +
+
+
+
{ + handleReset(); + }} + > + + Reset +
+
{ + handlePlayStop(); + }} + > + + {playSimulation ? "Play" : "Stop"} +
+
{ + handleExit(); + }} + > + + Exit +
+
+
+
0.5x
+
+
+
+
+
+
+
+
+
+
+
+
+ {speed.toFixed(1)}x +
+ +
+
+
50x
+
+
+
+ ); +}; + +export default SimulationPlayer; diff --git a/app/src/modules/market/AssetPreview.tsx b/app/src/modules/market/AssetPreview.tsx index a3c1cb0..be474a2 100644 --- a/app/src/modules/market/AssetPreview.tsx +++ b/app/src/modules/market/AssetPreview.tsx @@ -37,6 +37,7 @@ const AssetPreview: React.FC = ({
+ {/* Add canvas here */}
diff --git a/app/src/modules/market/Card.tsx b/app/src/modules/market/Card.tsx index 3df2e1f..646fe39 100644 --- a/app/src/modules/market/Card.tsx +++ b/app/src/modules/market/Card.tsx @@ -8,24 +8,51 @@ import { } from "../../components/icons/marketPlaceIcons"; import assetImage from "../../assets/image/image.png"; -const Card: React.FC = () => { + +interface CardProps { + assetName: string; + uploadedOn: string; + price: number; + rating: number; + views: number; + onSelectCard: (cardData: { + assetName: string; + uploadedOn: string; + price: number; + rating: number; + views: number; + }) => void; +} + +const Card: React.FC = ({ + assetName, + uploadedOn, + price, + rating, + views, + onSelectCard, +}) => { + const handleCardSelect = () => { + onSelectCard({ assetName, uploadedOn, price, rating, views }); + }; + return (
- + {assetName}
-
Asset name
-
Uploaded on-12 Jan 23
+
{assetName}
+
{uploadedOn}
- 1.5k + {views}
@@ -39,17 +66,17 @@ const Card: React.FC = () => {
- - - - - + {[...Array(5)].map((_, index) => ( + + ))}
- ₹ 36,500/unit + ₹ {price}/unit
-
Buy now
+
+ Buy now +
); }; diff --git a/app/src/modules/market/CardsContainer.tsx b/app/src/modules/market/CardsContainer.tsx index 27d4a6b..96a47e4 100644 --- a/app/src/modules/market/CardsContainer.tsx +++ b/app/src/modules/market/CardsContainer.tsx @@ -1,15 +1,149 @@ -import React from "react"; +import React, { useState } from "react"; import Card from "./Card"; +import AssetPreview from "./AssetPreview"; +import RenderOverlay from "../../components/templates/Overlay"; const CardsContainer: React.FC = () => { - const array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]; + const array = [ + { + id: 1, + name: "Asset 1", + uploadedOn: "12 Jan 23", + price: 36500, + rating: 4.5, + views: 500, + }, + { + id: 2, + name: "Asset 2", + uploadedOn: "14 Jan 23", + price: 45000, + rating: 4.0, + views: 500, + }, + { + id: 3, + name: "Asset 3", + uploadedOn: "15 Jan 23", + price: 52000, + rating: 4.8, + views: 500, + }, + { + id: 4, + name: "Asset 4", + uploadedOn: "18 Jan 23", + price: 37000, + rating: 3.9, + views: 500, + }, + { + id: 5, + name: "Asset 5", + uploadedOn: "20 Jan 23", + price: 60000, + rating: 5.0, + views: 500, + }, + { + id: 6, + name: "Asset 6", + uploadedOn: "22 Jan 23", + price: 46000, + rating: 4.2, + views: 500, + }, + { + id: 7, + name: "Asset 7", + uploadedOn: "25 Jan 23", + price: 38000, + rating: 4.3, + views: 500, + }, + { + id: 8, + name: "Asset 8", + uploadedOn: "27 Jan 23", + price: 41000, + rating: 4.1, + views: 500, + }, + { + id: 9, + name: "Asset 9", + uploadedOn: "30 Jan 23", + price: 55000, + rating: 4.6, + views: 500, + }, + { + id: 10, + name: "Asset 10", + uploadedOn: "2 Feb 23", + price: 49000, + rating: 4.4, + views: 500, + }, + { + id: 11, + name: "Asset 11", + uploadedOn: "5 Feb 23", + price: 62000, + rating: 5.0, + views: 500, + }, + { + id: 12, + name: "Asset 12", + uploadedOn: "7 Feb 23", + price: 53000, + rating: 4.7, + views: 500, + }, + ]; + + const [selectedCard, setSelectedCard] = useState<{ + assetName: string; + uploadedOn: string; + price: number; + rating: number; + views: number; + } | null>(null); + + const handleCardSelect = (cardData: { + assetName: string; + uploadedOn: string; + price: number; + rating: number; + views: number; + }) => { + setSelectedCard(cardData); + }; + return (
Products You May Like
- {array.map((index) => ( - + {array.map((asset) => ( + ))} + {/* */} + {selectedCard && ( + + )} + {/* */}
); diff --git a/app/src/pages/Project.tsx b/app/src/pages/Project.tsx index 95da878..ade4d6f 100644 --- a/app/src/pages/Project.tsx +++ b/app/src/pages/Project.tsx @@ -18,6 +18,7 @@ import { useNavigate } from "react-router-dom"; import { usePlayButtonStore } from "../store/usePlayButtonStore"; import SimulationUI from "../modules/simulation/simulationUI"; import MarketPlace from "../modules/market/MarketPlace"; +import SimulationPlayer from "../components/ui/simulation/simulationPlayer"; const Project: React.FC = () => { let navigate = useNavigate(); @@ -61,6 +62,7 @@ const Project: React.FC = () => { {activeModule !== "market" && } {/* */} + {isPlaying && activeModule === "simulation" && }
); }; diff --git a/app/src/styles/base/reset.scss b/app/src/styles/base/reset.scss index b37f940..82d286e 100644 --- a/app/src/styles/base/reset.scss +++ b/app/src/styles/base/reset.scss @@ -1,7 +1,14 @@ * { - margin: 0; - padding: 0; - box-sizing: border-box; - user-select: none; - font-size: var(--font-size-regular); + margin: 0; + padding: 0; + box-sizing: border-box; + user-select: none; + font-size: var(--font-size-regular); +} + +input[type="password"]::-ms-reveal, /* For Microsoft Edge */ +input[type="password"]::-ms-clear, /* For Edge clear button */ +input[type="password"]::-webkit-clear-button, /* For Chrome/Safari clear button */ +input[type="password"]::-webkit-inner-spin-button { /* Just in case */ + display: none; } diff --git a/app/src/styles/components/input.scss b/app/src/styles/components/input.scss index 3020c7d..36ac8ff 100644 --- a/app/src/styles/components/input.scss +++ b/app/src/styles/components/input.scss @@ -280,28 +280,24 @@ input { .dropdown-menu { position: absolute; - top: 100%; - left: 0; - background-color: #ffffff; - border: 1px solid #cccccc; + top: 110%; + right: -16px; + background-color: var(--background-color); + border: 1px solid var(--border-color); border-radius: 5px; - box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); + box-shadow: #{$box-shadow-medium}; z-index: 1000; min-width: 200px; overflow: auto; - max-height: 600px; + max-height: 400px; .dropdown-content { display: flex; flex-direction: column; gap: 6px; - .nested-dropdown { - // &:first-child{ margin-left: 0; - // } } - padding: 10px; } @@ -309,13 +305,13 @@ input { display: block; padding: 5px 10px; text-decoration: none; - color: #000000; + color: var(--text-color); font-size: 14px; cursor: pointer; transition: background-color 0.3s ease; &:hover { - background-color: #f0f0f0; + background-color: var(--background-color); } } @@ -329,15 +325,20 @@ input { padding: 5px 10px; cursor: pointer; font-size: 14px; - color: #000000; + color: var(--text-color); transition: background-color 0.3s ease; + border-radius: #{$border-radius-small}; + .arrow-container{ + @include flex-center; + } &:hover { - background-color: #f0f0f0; + background-color: var(--background-color); } &.open { - background-color: #e0e0e0; + color: var(--accent-color); + background-color: var(--highlight-accent-color); } .icon { @@ -349,7 +350,7 @@ input { .submenu { margin-top: 5px; padding-left: 20px; - border-left: 2px solid #cccccc; + border-left: 2px solid var(--border-color); display: flex; flex-direction: column; gap: 6px; @@ -576,26 +577,26 @@ input { color: var(--text-disabled); } } - .entered-emails{ + .entered-emails { @include flex-center; gap: 2px; background: var(--background-color-gray); padding: 0 4px; border-radius: #{$border-radius-large}; - span{ + span { height: 14px; width: 14px; line-height: 12px; text-align: center; border-radius: #{$border-radius-small}; - &:hover{ + &:hover { background: var(--accent-color); color: var(--primary-color); } } } } - .invite-button{ + .invite-button { padding: 4px 12px; border-radius: #{$border-radius-large}; background: var(--accent-color); diff --git a/app/src/styles/components/marketPlace/marketPlace.scss b/app/src/styles/components/marketPlace/marketPlace.scss index 2ae2790..cac366e 100644 --- a/app/src/styles/components/marketPlace/marketPlace.scss +++ b/app/src/styles/components/marketPlace/marketPlace.scss @@ -2,186 +2,356 @@ @use "../../abstracts/mixins.scss" as *; .marketplace-wrapper { - height: 100vh; - width: 100vw; - z-index: #{$z-index-marketplace}; - background-color: var(--background-color-secondary); - position: absolute; - left: 0; - padding: 100px 50px; + height: 100vh; + width: 100vw; + z-index: #{$z-index-marketplace}; + background-color: var(--background-color-secondary); + position: absolute; + left: 0; + padding: 100px 50px; + padding-bottom: 32px; + backdrop-filter: blur(6px); - .marketplace-container { - padding: 20px 2px; - height: calc(100vh - 120px); - background-color: var(--background-color); - box-shadow: #{$box-shadow-medium}; - border-radius: #{$border-radius-extra-large}; + .marketplace-container { + position: relative; + padding: 20px 2px; + height: 100%; + background-color: var(--background-color); + box-shadow: #{$box-shadow-medium}; + border-radius: #{$border-radius-extra-large}; + } + + .marketPlace { + width: 100%; + height: 100%; + overflow: auto; + left: calc(120px / 2); + top: 100px; + padding: 14px; + padding-bottom: 60px; + display: flex; + flex-direction: column; + gap: 24px; + + .filter-search-container { + width: 100%; + display: flex; + align-items: center; + gap: 12px; + + .search-wrapper { + min-width: 60%; + max-width: 684px; + padding: 0; + border-radius: $border-radius-large; + + .search-container { + border: none !important; + box-shadow: $box-shadow-medium; + border-radius: $border-radius-large; + + input { + border: none !important; + outline: none; + } + } + } + + .regularDropdown-container { + max-width: 159px; + } + + .button { + padding: 5px 20px; + border: 1px solid var(--accent-color); + border-radius: 14px; + } + + .rating-container { + display: flex; + align-items: center; + gap: 6px; + + .stars { + display: flex; + align-items: center; + } + } } - .marketPlace { - width: 100%; - height: 100%; - overflow: auto; - left: calc(120px / 2); - top: 100px; - padding: 14px; - padding-bottom: 60px; - display: flex; - flex-direction: column; - gap: 24px; + .cards-container-container { + padding: 0px 20px; + display: flex; + flex-direction: column; + gap: 6px; - .filter-search-container { + .header { + color: var(--text-color); + font-weight: $medium-weight; + font-size: $xlarge; + } + + .cards-wrapper-container { + display: flex; + flex-wrap: wrap; + gap: 28px; + + .card-container { + width: calc(25% - 23px); + border-radius: 18px; + padding: 12px; + box-shadow: 0px 2px 10.5px 0px #0000000d; + border: 1px solid var(--background-accent-transparent, #e0dfff80); + position: relative; + display: flex; + flex-direction: column; + justify-content: center; + gap: 6px; + + .icon { + position: absolute; + top: 12px; + left: 12px; + width: 30px; + height: 30px; + border-radius: 10px; + padding: 5px; + background-color: var(--accent-color); + } + + .image-container { width: 100%; display: flex; - align-items: center; - gap: 12px; + justify-content: center; + } - .search-wrapper { - min-width: 60%; - max-width: 684px; - padding: 0; - border-radius: $border-radius-large ; + .assets-container { + display: flex; + justify-content: space-between; - .search-container { - border: none !important; - box-shadow: $box-shadow-medium; - border-radius: $border-radius-large ; + .name-container { + display: flex; + flex-direction: column; + gap: 3px; - input { - border: none !important; - outline: none; + .asstes-container { + font-weight: #{$bold-weight}; + font-size: $regular; + } - } - } + .assets-date { + color: var(--accent-color); + font-size: $small; + } } - .regularDropdown-container { - max-width: 159px; - } + .details { + display: flex; + align-items: center; + gap: 10px; - .button { - padding: 5px 20px; - border: 1px solid var(--accent-color); - border-radius: 14px; - } - - .rating-container { + .content { display: flex; align-items: center; gap: 6px; - - .stars { - display: flex; - align-items: center; - - } + } } - } + } - .cards-container-container { - padding: 0px 20px; + .vendor-icon { + font-weight: #{$bold-weight}; + font-size: $regular; + } + + .stars-container { display: flex; - flex-direction: column; - gap: 6px; + justify-content: space-between; + } - .header { - color: var(--text-color); - font-weight: $medium-weight; - font-size: $xlarge; - } - - .cards-wrapper-container { - display: flex; - flex-wrap: wrap; - gap: 28px; - - .card-container { - width: calc(25% - 23px); - border-radius: 18px; - padding: 12px; - box-shadow: 0px 2px 10.5px 0px #0000000D; - border: 1px solid var(--background-accent-transparent, #E0DFFF80); - position: relative; - display: flex; - flex-direction: column; - justify-content: center; - gap: 6px; - - .icon { - position: absolute; - top: 12px; - left: 12px; - width: 30px; - height: 30px; - border-radius: 10px; - padding: 5px; - background-color: var(--accent-color); - } - - .image-container { - width: 100%; - display: flex; - justify-content: center; - } - - .assets-container { - display: flex; - justify-content: space-between; - - .name-container { - display: flex; - flex-direction: column; - gap: 3px; - - .asstes-container { - font-weight: #{$bold-weight}; - font-size: $regular ; - } - - .assets-date { - color: var(--accent-color); - font-size: $small; - } - } - - .details { - display: flex; - align-items: center; - gap: 10px; - - .content { - display: flex; - align-items: center; - gap: 6px; - } - } - } - - .vendor-icon { - - font-weight: #{$bold-weight}; - font-size: $regular ; - } - - .stars-container { - display: flex; - justify-content: space-between; - } - - .buy-now-button { - width: 100%; - background-color: var(--background-color-secondary); - border-radius: $border-radius-extra-large ; - padding: 8px 0; - @include flex-center; - color: var(--accent-color); - - &:hover { - cursor: pointer; - } - } - } + .buy-now-button { + width: 100%; + background-color: var(--background-color-secondary); + border-radius: $border-radius-extra-large; + padding: 8px 0; + @include flex-center; + color: var(--accent-color); + + &:hover { + cursor: pointer; } + } } + } } + } +} + +.assetPreview-wrapper { + width: 100%; + height: 100%; + position: absolute; + top: 0; + left: 0; + z-index: 3; + + .assetPreview { + width: 100%; + height: 100%; + background-color: var(--background-color); + display: flex; + gap: 12px; + z-index: 100; + border-radius: 20px; + } + + // Image Preview Section + .image-preview { + width: 50%; + height: 100%; + + img { + width: 100%; + height: 100%; + object-fit: contain; + } + } + + // Asset Details Section + .asset-details-preview { + width: 50%; + padding: 50px 20px; + overflow-y: auto; + } + + // Organization Section (Top part with image and details) + .organization { + display: flex; + align-items: center; + margin-bottom: 20px; + gap: 10px; + + .image { + @include flex-center; + height: 30px; + width: 30px; + min-height: 26px; + min-width: 26px; + border-radius: 50%; + font-weight: var(--font-weight-bold); + color: var(--background-color); + background-color: var(--accent-color); + } + + .organization-details { + display: flex; + flex-direction: column; + + .organization-name { + font-weight: bold; + margin-bottom: 5px; + + font-weight: #{$bold-weight}; + font-size: $regular; + } + + .follow { + color: var(--accent-color); + cursor: pointer; + } + } + } + + // Asset Details + .asset-details { + margin-top: 20px; + + .asset-name { + font-size: 1.5em; + font-weight: bold; + margin-bottom: 10px; + font-weight: #{$bold-weight}; + font-size: $large; + } + + .asset-description { + margin-bottom: 20px; + color: #666; + } + + .asset-review { + width: fit-content; + padding: 5px 10px; + display: flex; + align-items: center; + margin-bottom: 20px; + outline: 1px solid #909090cc; + border-radius: 6px; + + .asset-rating { + display: flex; + align-items: center; + gap: 4px; + margin-right: 10px; + font-weight: bold; + position: relative; + + font-weight: #{$bold-weight}; + font-size: $regular; + + &::after { + margin-left: 5px; + content: ""; + display: block; + width: 2px; + height: 12px; + background-color: #ccc; + } + } + + .asset-view { + font-weight: #{$bold-weight}; + font-size: $regular; + } + } + + .asset-price { + font-size: $xxlarge; + font-weight: bold; + margin-bottom: 20px; + } + } + + // Button Container and Button Styles + .button-container { + display: flex; + gap: 10px; + } + + .button { + color: white; + padding: 10px 20px; + border-radius: 5px; + cursor: pointer; + text-align: center; + + &:first-child { + outline: 1px solid var(--accent-color); + color: var(--accent-color); + } + + &:last-child { + background-color: var(--accent-color); + color: var(--background-color); + } + } + + .closeButton { + color: var(--accent-color); + position: absolute; + top: 18px; + left: 18px; + @include flex-center; + cursor: pointer; + font-size: var(--font-size-large); + } } diff --git a/app/src/styles/components/simulation/simulation.scss b/app/src/styles/components/simulation/simulation.scss new file mode 100644 index 0000000..eb109c2 --- /dev/null +++ b/app/src/styles/components/simulation/simulation.scss @@ -0,0 +1,114 @@ +@use "../../abstracts/variables" as *; +@use "../../abstracts/mixins" as *; + +.simulation-player-wrapper { + position: fixed; + bottom: 32px; + left: 50%; + z-index: 2; + transform: translate(-50%, 0); + .simulation-player-container { + .controls-container { + @include flex-center; + gap: 12px; + margin-bottom: 4px; + .simulation-button-container { + @include flex-center; + gap: 2px; + padding: 6px 8px; + min-width: 64px; + background-color: var(--background-color); + border-radius: #{$border-radius-small}; + cursor: pointer; + &:hover { + background-color: var(--highlight-accent-color); + color: var(--accent-color); + path { + stroke: var(--accent-color); + } + } + } + } + .speed-control-container { + @include flex-center; + gap: 18px; + padding: 5px 16px; + background: var(--background-color); + border-radius: #{$border-radius-medium}; + box-sizing: #{$box-shadow-medium}; + .min-value, + .max-value { + font-weight: var(--font-weight-bold); + } + .slider-container { + width: 580px; + max-width: 80vw; + height: 28px; + background: var(--background-color-gray); + border-radius: #{$border-radius-small}; + position: relative; + padding: 4px 26px; + .custom-slider { + height: 100%; + width: 100%; + position: relative; + .slider-input { + position: absolute; + width: 100%; + height: 100%; + opacity: 0; + z-index: 3; + cursor: pointer; + } + .slider-handle { + position: absolute; + width: 42px; + line-height: 20px; + text-align: center; + background: var(--accent-color); + color: var(--primary-color); + border-radius: #{$border-radius-small}; + transform: translateX(-50%); + cursor: pointer; + z-index: 2; + } + } + .marker{ + position: absolute; + background-color: var(--text-disabled); + width: 2px; + height: 12px; + border-radius: 1px; + top: 8px; + } + .marker.marker-10{ + left: 10%; + } + .marker.marker-20{ + left: 20%; + } + .marker.marker-30{ + left: 30%; + } + .marker.marker-40{ + left: 40%; + } + .marker.marker-50{ + left: 50%; + } + .marker.marker-60{ + left: 60%; + } + .marker.marker-70{ + left: 70%; + } + .marker.marker-80{ + left: 80%; + } + .marker.marker-90{ + left: 90%; + } + } + } + } +} diff --git a/app/src/styles/components/visualization/ui/styledWidgets.scss b/app/src/styles/components/visualization/ui/styledWidgets.scss index 6006308..9c72fae 100644 --- a/app/src/styles/components/visualization/ui/styledWidgets.scss +++ b/app/src/styles/components/visualization/ui/styledWidgets.scss @@ -8,7 +8,7 @@ flex-direction: column; gap: 6px; width: 100%; - min-width: 250px; + // min-width: 1450px; .header { display: flex; justify-content: center; diff --git a/app/src/styles/main.scss b/app/src/styles/main.scss index fa6ce51..34c0074 100644 --- a/app/src/styles/main.scss +++ b/app/src/styles/main.scss @@ -20,8 +20,9 @@ @use 'components/tools'; @use 'components/visualization/floating/energyConsumed'; @use 'components/visualization/ui/styledWidgets'; -@use './components/visualization/floating/common'; -@use './components/marketPlace/marketPlace.scss'; +@use 'components/visualization/floating/common'; +@use 'components/marketPlace/marketPlace'; +@use 'components/simulation/simulation'; // layout @use 'layout/loading'; diff --git a/app/src/styles/pages/realTimeViz.scss b/app/src/styles/pages/realTimeViz.scss index f4c9ab9..4c4c9ae 100644 --- a/app/src/styles/pages/realTimeViz.scss +++ b/app/src/styles/pages/realTimeViz.scss @@ -1,4 +1,5 @@ @use "../abstracts/variables.scss" as *; +@use "../abstracts/mixins.scss" as *; // Main Container .realTime-viz { @@ -35,7 +36,7 @@ } } - .zoon-wrapper { + .zone-wrapper { display: flex; background-color: var(--background-color); position: absolute; @@ -43,15 +44,32 @@ left: 50%; transform: translate(-50%, 0); gap: 6px; - padding: 4px; + border-radius: 8px; max-width: 80%; overflow: auto; - max-width: calc(100% - 450px); + // max-width: calc(100% - 450px); + &::-webkit-scrollbar { display: none; } + .arrow { + background-color: var(--highlight-accent-color); + color: var(--background-color); + } + + .zones-wrapper { + padding: 6px; + display: flex; + gap: 6px; + border-radius: #{$border-radius-medium}; + overflow-x: auto; + &::-webkit-scrollbar { + display: none; + } + } + .zone { width: auto; background-color: var(--background-color); @@ -68,28 +86,10 @@ } } - .zoon-wrapper.bottom { + .zone-wrapper.bottom { bottom: 210px; } - @media (max-width: 1024px) { - width: 80%; // Increase width to take more space on smaller screens - height: 500px; // Reduce height to fit smaller screens - left: 50%; // Center horizontally - - .main-container { - margin: 0 15px; // Reduce margin for better spacing - } - - .zoon-wrapper { - bottom: 5px; // Adjust position for smaller screens - - &.bottom { - bottom: 150px; // Adjust for bottom placement - } - } - } - .content-container { display: flex; height: 100vh; @@ -104,7 +104,7 @@ margin: 0 30px; transition: height 0.3s ease, margin 0.3s ease; - .zoon-wrapper { + .zone-wrapper { display: flex; background-color: rgba(224, 223, 255, 0.5); position: absolute; @@ -150,15 +150,15 @@ transition: all 0.3s ease; border-radius: 6px; overflow: visible !important; + z-index: $z-index-tools; .panel-content { position: relative; height: 100%; padding: 10px; - overflow: auto; display: flex; flex-direction: column; - gap: 10px; + gap: 6px; background-color: var(--background-color); &::-webkit-scrollbar { @@ -167,8 +167,7 @@ .chart-container { width: 100%; - height: 24% !important; - + height: 25% !important; min-height: 150px; max-height: 100%; border: 1px dotted #a9a9a9; @@ -176,6 +175,70 @@ box-shadow: 0px 2px 6px 0px rgba(60, 60, 67, 0.1); padding: 6px 0; background-color: white; + position: relative; + + .kebab { + width: 30px; + height: 30px; + position: absolute; + top: 0px; + right: 0px; + z-index: 10; + cursor: pointer; + @include flex-center; + } + + .kebab-options { + position: absolute; + top: 12px; + right: -100px; + transform: translate(0px, 0); + background-color: var(--background-color); + z-index: 10; + + display: flex; + flex-direction: column; + gap: 6px; + border-radius: 4px; + + box-shadow: var(--box-shadow-medium); + + .btn { + display: flex; + gap: 6px; + align-items: center; + padding: 5px 10px; + color: var(--text-color); + + &:hover { + .label { + color: var(--accent-color); + } + } + + &:hover { + background-color: var(--highlight-accent-color); + width: 100%; + + svg { + &:first-child { + fill: var(--accent-color); + } + + &:last-child { + fill: auto; + stroke: var(--accent-color); + } + } + } + } + + .btn-blur { + color: var(--text-disabled); + cursor: not-allowed; + pointer-events: none; + } + } } .close-btn { @@ -194,8 +257,6 @@ left: 0; right: 0; - - .panel-content { display: flex; flex-direction: row; @@ -236,19 +297,21 @@ } } -.playingFlase{ - .zoon-wrapper{ - bottom: 300px !important; +.playingFlase { + .zone-wrapper.bottom { + bottom: 300px; } } + // Side Buttons .side-button-container { position: absolute; display: flex; background-color: var(--background-color); - padding: 5px; - border-radius: 8px; + padding: 2px; + border-radius: 2px; transition: transform 0.3s ease; + box-shadow: #{$box-shadow-medium}; .extra-Bs { display: flex; @@ -285,6 +348,24 @@ border: none; color: var(--background-color); border-radius: 4px; + .add-icon { + @include flex-center; + transition: rotate 0.2s; + } + path { + stroke: var(--primary-color); + stroke-width: 2; + } + } + .active { + background: #ffe3e0; + .add-icon { + rotate: 45deg; + path { + stroke: #f65648; + stroke-width: 2; + } + } } &.top { @@ -401,3 +482,33 @@ } } } + +.arrow { + position: absolute; + top: 50%; + transform: translateY(-50%); + border: none; + cursor: pointer; + z-index: 10; + height: 100%; +} + +.left-arrow { + left: 0; +} + +.right-arrow { + right: 0; +} + +.zone { + padding: 10px; + border: 1px solid #ccc; + border-radius: 5px; + cursor: pointer; +} + +.zone.active { + background-color: #007bff; + color: white; +} diff --git a/compose.yaml b/compose.yaml index 33328bc..2b5e419 100644 --- a/compose.yaml +++ b/compose.yaml @@ -1,24 +1,24 @@ -services: - frontend: - build: - context: ./app - dockerfile: Dockerfile - args: - - REACT_APP_SERVER_SOCKET_API_BASE_URL=185.100.212.76:8000 - - REACT_APP_SERVER_REST_API_BASE_URL=185.100.212.76:5000 - - REACT_APP_SERVER_MARKETPLACE_URL=185.100.212.76:50011 - container_name: dwinzo-beta - stdin_open: true - tty: true - ports: - - "8200:3000" - environment: - - WDS_SOCKET_PORT=0 - - PORT=3000 - - DOCSIFY_PORT=8201 - volumes: - - ./app:/app - -volumes: - frontend: - driver: local +services: + frontend: + build: + context: ./app + dockerfile: Dockerfile + args: + - REACT_APP_SERVER_SOCKET_API_BASE_URL=185.100.212.76:8000 + - REACT_APP_SERVER_REST_API_BASE_URL=185.100.212.76:5000 + - REACT_APP_SERVER_MARKETPLACE_URL=185.100.212.76:50011 + container_name: dwinzo-beta + stdin_open: true + tty: true + ports: + - "8200:3000" + environment: + - WDS_SOCKET_PORT=0 + - PORT=3000 + - DOCSIFY_PORT=8201 + volumes: + - ./app:/app + +volumes: + frontend: + driver: local